Commit Graph
26 Commits
Author SHA1 Message Date
GaoYiYuan_0626 e04b44c44a 基金转换 T+1 模型:T-9 api 层(受理 202 + 撤单/确认/查询接口 + 网关分派 + redeem 份额申报)
三接口路径以 PRD 为准(裁定 11/12):
- app/api/convert_admin.py 新增 POST /api/simulate/trade/convert/{gid}/cancel(交易 owner 闸门)
  + POST /api/admin/convert/confirm?accept_date=(risk_officer)+ GET /api/simulate/trade/convert/{gid}(查询 scope 闸门)
- simulate.py convert 分支受理 202 + 受理回执(两类 202 分辨 accepted/processing)
- trade_gateway 分派改调 accept_convert(SUPPORTED_TRADE_TYPES 维持 2 类型)
- redeem 份额申报(D26/R-6:qty 入参,_redeem_quote = qty * T 净值 - 赎回费)
- errors.py 新增 CANCEL_NOT_ALLOWED / CONCURRENT_CONFLICT(均 409)

修正 2 处口径缺陷(测试打穿揭示,非只改测试):
- _rebuild_quote 的 out_nav 改直读 core_convert_lot_detail.nav(反推致 1.3604 -> 1.3600 漂移)
- _accept_idempotent 字段面扩展与受理回执对齐(幂等重试客户端可取到预计确认日)

验证:端到端 13 passed(T+1 两段链路重写);全量 834 passed / 10 skipped;
真库 verify_convert_api.py 88/88,存量 4 脚本零回归;3 组突变验证命中后还原
2026-09-11 20:37:45 +08:00
GaoYiYuan_0626 57f16a50a1 基金转换 T+1 模型:T-8 引擎时机收口(调用点唯一性守护 + 受理不触引擎 + 真库链路切 T+1)
【范围澄清】
引擎调用点在 T-7 即已迁到确认事务 commit 之后(engine_call.run_convert_engine 唯一出口),
受理段 accept_convert 自实现起就不含引擎调用 → R-7 已满足。本轮补齐**守护与端到端验证**。
注意 F-10 已明确:T-8 **不动**去重口径(amount_view 三处消费方原样不动,那是 T-11 的范围)。

【新增测试】
- test_convert_engine.py:test_process_convert_event_has_single_call_site —— AST 统计
  app/ 下 process_convert_event( 的 Call 节点**恰 1 处**且宿主为 run_convert_engine。
  用 AST 而非字符串匹配:engine_call.py 的说明段含该字样,会被误判。
  **已做突变验证**(临时加第二处调用 → 断言变红,随后还原)。
- test_convert_accept.py:受理无副作用用例补「不触引擎」断言 —— 受理后 risk_alert 0 行、
  无 pass / engine_error 审计(验收 29 前半)。
- test_convert_confirm.py: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):
受理 accept_convert → 确认 confirm_one;新增 A0 组「受理不触引擎」;A~F 组保留原断言
(引擎出单 / RISK-002 夹逼 / 去重不删行 / 幂等重放 / 无命中 / 清理自检)。真库 39/39 一致。
- 折算期望改用 T 日净值(未知价法),不再沿用批次买入净值 alloc.nav;
- 基准日取真库日历中位开市日,T+1 = 下一交易日;
- 阈值必须显式传给 confirm_one —— 缺省退回 settings 默认会把 RISK-002 误判为命中,
  症状像「去重失效」,实为口径没传(首轮实跑被 B 组断言捕获,已钉进注释)。

【回归】
全量 829 passed / 10 skipped(827 → +2);复跑 T-6 verify_convert_accept 37/37、
T-7 verify_convert_confirm 83/83、v1.0 链路 verify_convert_service 35/35,均零失败。
2026-09-11 19:48:53 +08:00
GaoYiYuan_0626 0e27fec5cb 文档:T-7 确认段落地回写(开发计划 DoD 全勾 + 执行记录 / 交接文档 §B.11 / TODO / 两处 MEMORY)
- 开发计划 T-7:DoD 9 项勾选 + 新增「执行记录」小节,逐条留痕 6 处与计划的差异裁定:
  ① 确认段不做最低持有重判(计划缺口,强制全转属受理段决策,受理单 remark 传承);
  ② 新增 format/audit/engine_call 三共用模块(后者顺带完成 T-8 的「引擎时机改」);
  ③ nav.py 未改(确认段直调 core_ro.get_nav_on,取 T−1 路径随 v1.0 convert_fund 一并删);
  ④ test_convert_integration 改写推迟到 T-9(需端到端真接口);
  ⑤ 受理单置 confirmed 并入 apply_convert 同事务(防「批次已扣、状态未置」中间态);
  ⑥ MySQL rowcount=changed rows 陷阱(nav_pending 重试不得复用冲突判定)。
- 交接文档 §B.11:T-6→T-7 完成、基线 797→827、真库 36/36→37/37 与 83/83、
  新增 T-7 实施期发现段、下一步 T-8/T-9(并注明 T-8 范围已缩小)。
- docs/memory/TODO.md:进度行 + T-6 真库数 + T-7 条目勾选与详述。
- .workbuddy/memory/MEMORY.md 与 docs/memory/MEMORY.md:进度与关键裁定同步;
  当日工作日志补「续 3:T-7 确认段落地」。
2026-09-11 19:37:30 +08:00
GaoYiYuan_0626 048f1a9e1e 基金转换 T+1 模型:T-7 确认段落地(confirm_service 三段编排 + T+1 批处理)
【新增】
- app/service/convert/confirm_service.py:confirm_one(8 步确认)+ confirm_batch
  (按业务日捞单 / 整批锁 / 串行 / 单笔容错)。关键裁定:
  · T 日净值用 get_nav_on 精确匹配,缺则 nav_pending(绝不回退旧净值);
  · 扣批次 + 2 条流水 + 转入批次 + 两端持仓 + 明细 + 受理单置 confirmed
    同处一个 Core 单库事务,状态被抢(rowcount!=1)→ 整事务回滚;
  · 引擎在事务 commit 后跑,异常不阻断已成立的交易(FR-C28);
  · 部分成交 actual=min(申请,可用),被抢部分占用自然释放(R-10)。
- app/service/convert/format.py / audit.py / engine_call.py:展示规格、审计、
  引擎调用三处共用出口抽出(受理/确认两段不再各写一份,避免口径漂移)。
- scripts/dev/verify_convert_confirm.py:真库验证 81 项断言,含 PRD §5.3.2
  示例在真库上逐字节重放(68020.00/612.18/67407.82/333.36/67074.46/34945.54/-0.0049)。

【修复 · 受理-确认接口契约缺口】
强制全转是**受理段决策**(受理时 qty 已收敛为实际全转量),确认段拿不到原始
申请量、无法复现该判定。修法:
- 受理段把 forced_full_transfer 落受理单 remark(新增 REMARK_FULL_TRANSFER);
- 确认段改为**继承受理决策、不再重判最低持有**(plan_lots 不传 min_hold_qty)
  —— 重判会因 T→T+1 可用份额变化得出与受理承诺不一致的结论(擅自扩大客户指令);
- remark 支持多标记 `;` 连接(full_transfer;partial)。
- MySQL rowcount=changed rows 陷阱:nav_pending 重试不得复用 transition_status
  的冲突判定,改为 status 未变时不迁移、返回 transitioned=False。

【其他】
- convert_service:新增 cancel_convert(T 日撤单,两道闸门)、_t1_t2_dates
  (日历边界 None 容错);受理响应改为 PRD §5.3.1 字段;convert_fund 标 Deprecated。
- convert_core_repository:ConvertApplyInput 加 convert_request_id/diff_fee/
  request_remark;_apply_convert_once 末步 _confirm_request 事务内置状态守卫。
- convert_request_repository:_end_of_day 统一闭区间语义;新增 reject()。
- 测试:新增 tests/test_convert_confirm.py(24 例);全量 827 passed / 10 skipped。
2026-09-11 19:35:37 +08:00
GaoYiYuan_0626 f056fcd007 基金转换 T+1 模型:T-1~T-6 落地(DDL/数据层/语义收窄/锁/受理事务)
AIcoding 第 5 步 todo 开发(开发计划 v2.0)前半段:

- T-1 3 张新表 DDL(core_convert_request 6 态 ENUM / core_trade_calendar /
  core_share_rule)+ 种子 + sqlite 单一事实源同步 + 列清单断言
- T-2/T-2b calc 扩展(product_round/redeem_amount/partial_qty)+ 真实净值实算回填
- T-3 convert_request_repository(6 态 + 条件 UPDATE 守卫)+ core_ro 三读方法
  + share_lot_repository.available_qty_with_inflight(R-3 在途占用推导)
- T-4 convert_repository.sync_mirror 成为 risk_convert_detail 唯一进度镜像写入口
  (旧三方法标 Deprecated,T-7 后删)
- T-5 locks.py 锁键构造器 convert_req_lock_key / convert_confirm_lock_key
- T-6 convert_service.accept_convert 受理事务(八步:锁→幂等→校验→受理日顺延
  →在途占用校验→落单→镜像+审计→202;不扣份额/不折算/不写流水)
  + tests/test_convert_accept.py(16 用例)
  + scripts/dev/verify_convert_accept.py(真库 36/36 一致)
  + trading_calendar.py 纯函数包(R-5)+ 21 用例

T-6 真库实测暴露并修复:confirm_eta 在日历数据边界抛 ValueError,会让已落库
的受理单在调用方眼里变 500;改为展示性字段容错 + 单测守护。

基线:798 passed / 10 skipped,零回归。
2026-09-11 19:21:15 +08:00
GaoYiYuan_0626 22f2a41192 基金转换 T-12:补偿脚本(详情 + 预警)
阶段二失败(或阶段 1.5 引擎失败)后,仅凭 Core 侧数据把「详情 + 预警」两件事补回来
(PRD §7.1 / 架构 §5.4)。

落地(改 3 + 新增 2 脚本 + 测试 2 文件)
- convert_service:新增公开 compensate_convert(group_id, ...) —— 补偿的服务端单点入口
  · 锁键 convert:rerun:{gid},与 convert_fund 幂等重试路径同一个键
  · 详情侧:非 completed 才补写,复用 _finalize_from_core(不另写第二份阶段二)
  · 预警侧:幂等锚点 = 转出端 out_trade_id,复用 find_alerts_by_trade;
    命中即 skipped,否则跑 process_convert_event
- risk_repository:has_engine_error_audit 加 decision 参数(默认值不变)
  · convert 线阶段 1.5 失败审计用 engine_error,普通交易用 risk_engine_error,不是同一个码
- rebuild_alerts.py:新增 --convert-group(与 location 参数 trade_ids 互斥),薄封装
- 新增 scripts/agent/cleanup_pending_convert.py(架构 §2 与开发计划 §9 指定路径):
  超 convert_compensate_sla_hours 的 pending 占位 → status='expired'(标记不硬删,S2)
- 新增 scripts/dev/verify_convert_compensate.py:真库验证脚本(MySQL 8.0.46)
- 测试 +12:test_convert_service +4(补写 / 幂等 / missing / locked)、
  test_demo_scripts +8(--convert-group 分派与接线 + cleanup 脚本)

state 四态与 CLI 退出码
- rebuilt(0) / skipped(0 幂等) / missing(1 零写入) / locked(3)
- 退出码 2 保留给 argparse 用法错误,故 locked 取 3

真库专属证据(sqlite 单测给不了的,本任务核心增量)
- status='expired' 在 MySQL ENUM 上被接受(sqlite 该列是 VARCHAR,写什么都收)
- created_at < cutoff 在 DATETIME(3) 上的时间边界正确(超时进候选 / 未超时不进 / 复跑幂等)
- input_summary 是真 JSON 列,而 has_engine_error_audit 用 LIKE 判定:脚本先断言
  information_schema 的 DATA_TYPE='json' 再验命中,并反向断言决策码不匹配则不命中

顺带收口(用户指示)
- core_ro.concentration_profile 补 h.qty > 0,与 list_holdings 真正同口径
  · ratio 不变(归零行市值为 0),但 rows 不再多出已清仓产品、不虚占截断判定位
  · 新用例含跨出口一致性断言;突变验证:去掉 qty > 0 → 精准 1 条红

验证
- pytest -q → 731 passed / 3 skipped(基线 719 加 12,零回归)
- 突变验证 3 组精准命中:去掉幂等锚点(1 红)/ 去掉 status 过滤(2 红)/ 补偿无视锁(1 红)
- 真库 verify_convert_compensate.py 34/34,隔离数据零残留
- 全套 7 个真库脚本复跑零回归:seed 全 PASS / apply 24 / service 35 / engine 31 / lots 20 / tools 14 / compensate 34
2026-09-10 18:59:42 +08:00
GaoYiYuan_0626 a6ac39edd6 基金转换 T-11:工具与 SQL 汇总去重(FR-C15 / R-d)
convert 在 core_trade 落两条流水(转出 redeem + 转入 subscribe,共享 convert_group_id),
所有金额聚合读取方必须只计一次,否则同笔转换金额翻倍。

落地(改码 3 处 + 新增 1 脚本 + 测试 3 文件)
- rules.py:_amount_view → 公开 amount_view(提升而非复制别名,全仓唯一金额聚合口径);
  docstring 补「跨模块共用」说明
- core_tools.py:query_recent_trades 的 sum_amount 改走 amount_view(FR-C15);
  items / total_count 保持全量不变——两条流水是真实的两笔权益变动,读到两条是对的
- core_ro.py:list_holdings 加 h.qty > 0(convert 转出全部份额留下的 qty=0 归零行不是持仓);
  sum_trades_on_date 加 convert 去重条件(R-d),组内只计转出端
- 新增 tests/test_core_tools.py(3 条:汇总不翻倍 / 跨口径一致性 / 持仓不返回 qty=0 行)
- tests/test_core_ro_sum.py 追加 1 条 convert 去重用例(既有断言零改动)
- 新增 scripts/dev/verify_convert_tools.py:真库验证脚本(MySQL 8.0.46)

执行期裁定 2 条(已留痕)
- _amount_view 提升为公开而非复制:一个函数两个名字会漂移(自检第 13 问)
- SQL 条件从 IS NULL 扩为 IS NULL OR = '':Python 的 if not gid 把空串当无组,
  而 SQL 里 '' IS NULL 恒假。真库实证:只写 IS NULL → 合计数 400000(正确 450000,漏算 50000)。
  不加则 RISK-002 漏算,且 sqlite 单测若只造 NULL 数据永远发现不了
- 等价性边界:组内无 redeem 时 SQL 丢整组、amount_view 保留首条;由 R-b 保证不可达

验证
- pytest -q → 718 passed / 3 skipped(基线 714 加 4,零回归)
- 突变验证 3 组均精准命中:去掉去重条件(2 红)/ 去掉 qty>0(1 红)/ 汇总不走 amount_view(2 红)
- 真库 verify_convert_tools.py 14/14,隔离数据零残留
- 复跑受影响真库脚本零回归:T-8 31/31、T-7 35/35、T-10 20/20
2026-09-10 18:40:27 +08:00
GaoYiYuan_0626 5e6fa06d03 基金转换 T-10:普通申赎批次维护(FR-C16)
让 core_trade 的普通申赎同步维护 core_share_lot 批次,为 convert 提供权威份额口径。

落地(改码 3 处 + 新增 2 个脚本)
- gateway_repository:新增 insert_share_lots / deduct_share_lots,各自单事务、走 rw 账号;
  只落已分配结果、不含分配逻辑,避免出现第二份 FIFO 口径
- share_lot_repository:__init__ 增可选 core_ro 入参,让 trade_gateway 复用同一份 FIFO 选批口径(D18)
- trade_gateway:新增 _nav_as_of / _maintain_lots,在落流水后、规则引擎前调用
  · subscribe:取 T 日(含)前最新净值 → qty = amount ÷ 净值(2 位 HALF_UP)→ 建批次(confirmed_at = T)
  · redeem:FIFO 扣减;无批次但有持仓 → D8 兜底补建后再扣;两者皆无 → 降级跳过
  · 整段 try 包住,任何异常只 warning,绝不阻断交易(R-c(1))
- 新增 scripts/core/rebuild_lots.py:按 core_holding 快照重建批次(L-7),与网关 D8 同调 bootstrap_lots;
  默认只补建无批次的持仓行(幂等),--force 才先删后建,--dry-run 只报告
- 新增 scripts/dev/verify_convert_lots.py:真库验证脚本(MySQL 8.0.46)

口径订正(联网查证 4 家管理人业务规则后)
- redeem 的 amount ÷ 净值 折算属本项目简化建模,不是行业标准
- 行业铁律是「金额申购、份额赎回」:投资者以份额申报,登记机构按 T 日净值算金额
  (睿远业务规则 §65 / 华泰保兴 §69 / 东方基金 §57 / 国投瑞银 §33;无一家公募支持按金额赎回)
- 派生风险:未知价法下 T 日净值当日不可得(T+1 公告),_nav_as_of 实取 T−1 净值,
  故此处算出的份额只是估算值
- 已记入 PRD §10.1 已知差异清单;trade_gateway 注释同步订正

验证
- pytest -q → 714 passed / 3 skipped(基线 697 加 17,零回归)
- R16 test_redeem_accepted_without_alert 零改动通过(由 R-c(1) 降级保住)
- 真库 20/20;T-6 24/24、T-7 35/35 复跑零回归;真库隔离数据零残留
- 突变验证 2 组:切断接线 → 精准 2 条红;关掉 D8 兜底 → 精准 2 条红(含 D18 同源断言)
2026-09-10 18:28:48 +08:00
GaoYiYuan_0626 7ad1c8204d 基金转换 T-8:规则引擎改造(_amount_view 去重视图 + process_convert_event)
一次转换落两条流水(转出 redeem + 转入 subscribe,同 convert_group_id),
金额聚合类规则若两条都算会翻倍报假预警,故按「逐笔 / 聚合」拆成两个视图。

- rules.py:新增 _amount_view —— 同组内只保留 redeem 那条(无 convert_group_id
  的交易恒等通过、组内无 redeem 保首条、绝不删行);run_rules 双视图分流:
  RISK-001/003/004 用全量 eligible(逐笔判定),RISK-002/005 走金额视图去重。
- alert_service.py:抽出公开 build_trade_event(事件体结构唯一定义),
  record_trade_alerts 新增可选 events 参数;缺省 None 退化为单条,
  既有调用零改动。新建单落 payload.events 全部,聚合追加只追首条(只认转出端)。
- engine.py:抽 _run 共用实现;process_trade_event 变薄封装(签名与行为不变);
  新增 process_convert_event(out_trade, in_trade, ...) 与 _notify_error_hook
  (hook 自身异常吞掉,原始异常照常上抛)。

实质影响:阶段 1.5 从「ImportError 静默跳过」变为「真跑」——T-7 部署时
process_convert_event 不存在,T-8 落地后同一笔转换会真实出单 + 写 L3 + 落审计;
T-7 真库脚本复跑仍 35/35,无连带破坏。

验证:新增 tests/test_convert_engine.py(15 用例)+ test_convert_service.py
接线回归 1 条;pytest 672 passed / 3 skipped(基线 656 +16,零回归);
新增 scripts/dev/verify_convert_engine.py 真 MySQL 验证 31/31;
突变验证(关掉去重视图 → 4 条变红)确认用例非假绿。
2026-09-10 17:34:35 +08:00
GaoYiYuan_0626 318cb39a1f 基金转换 T-7:convert_service 八步编排(关键路径 · 幂等前置 + 三阶段)
- 新增 app/service/convert/convert_service.py:八步编排(①-④ 校验不落库 / ⑤ 占位 /
  ⑥ apply_convert / ⑦ 阶段 1.5 引擎不阻断 / ⑧ 回写 + 审计),幂等判定前置到校验之前
  以修复同键重试误报 InsufficientShares
- 新增 tests/test_convert_service.py(17 用例)
- 新增 scripts/dev/verify_convert_service.py(真 MySQL 验证 35/35)
- 改 app/repository/convert_repository.py:complete_convert 由纯 UPDATE 改三步法
  upsert(无占位直跑也能落完成行,R-a 口径)
- 改 app/repository/core_ro.py:新增 has_convert_trades / list_convert_trades /
  list_convert_lot_details 三个只读方法
- 改 app/config/settings.py:新增 6 个 convert 配置项
- 修 tests/test_convert_calc.py:TestPurity 排除编排层 convert_service.py

基线 639 → 656 passed / 3 skipped,零回归
2026-09-10 17:12:01 +08:00
GaoYiYuan_0626 82c9467fb7 基金转换 T-6:apply_convert 阶段一单事务(core 库唯一写入口)
新增 app/gateway/convert_core_repository.py:两条流水(redeem/subscribe,
R-b 不使用 ENUM 的 convert)+ N×条件 UPDATE 批次扣减 + 转入新批次
+ 两端 core_holding + N×计费明细,全部收口在 with engine.begin() 单事务。

实现级要点(与开发计划 §6.1 的三处差异,已回写该节执行记录):
- core_holding 改用「基于列当前值的增量 UPDATE」,入参不带持仓快照:
  MySQL 的 UPDATE 是当前读,并发两笔自然累加;用快照算绝对值会互相覆盖。
- 数值参数参与算术时写成 (:x + 0.0):实测 sqlite 在 UPDATE 算术表达式中
  不把 TEXT 绑定参数转数值(传 '120' 时 c 不变),加 +0.0 后两库一致。
- pnl_pct 拆成独立 UPDATE 重算,避开 MySQL「SET 从左到右」的顺序坑。

验证:pytest 639 passed / 3 skipped(基线 634 +5,零回归);
新增 scripts/dev/verify_convert_apply.py 真 MySQL 验证 24/24 一致,
含真并发两笔首次转入同一产品的终态断言(MySQL RR 的 gap lock 实测留痕)。
2026-09-10 16:26:42 +08:00
GaoYiYuan_0626 c5182f1910 feat(convert): 基金转换 T-0/T-0b 门禁 + T-1 数据层 + T-2/T-2b 纯函数与实算回填
T-0 / T-0b(门禁 · 2026-09-10)
- T-0:sqlite 与 MySQL 结构对齐 —— core_holding 统一为 qty/cost_amount/as_of/pnl_pct
  + PK + UNIQUE(customer_id, product_id);补 core_product_nav;新增建库自校验
  _assert_ddl_aligned()(R-g);test_db.py 增 3 条门禁用例(含反向验证门禁失效)
- T-0b:DB 账号分离(D20)—— 新增 scripts/core/00-grant.sql(三账号逐表授权);
  settings.py 增 3 组账号;db.py 改 get_engine(db, role),缓存键改为 (库名, 角色),
  账号未配置回退单账号;core_ro→ro / gateway_repository→rw / risk·session_repository→rw;
  tests/conftest.py 四处显式 role="admin"(R-e)

T-1(数据层)
- scripts/core/01-ddl.sql:新建 core_fee_rule / core_share_lot / core_convert_lot_detail;
  core_trade 加 convert_group_id + idx_convert_group;core_product 加 8 列 + fee_rate 补 COMMENT
- 新增 07-seed-fee-rule.sql(赎回费 5 档 × 14 产品,按 22 号文 §10)/ 08-seed-share-lot.sql
  (58 行持仓 → 61 行批次,Σ remain_qty 恒等于 qty)/ 09-seed-org.sql(管理人 + TA +
  申购费率 + 最低持有余额,v1.1 按「管理人全产品线」重排)
- reset.ps1 追加 07/08/09;02-mysql-agent专用.sql 追加 risk_convert_detail
- tests/_ddl.py 同步 4 表 + 新增 REQUIRED_CONVERT_TABLES 建库门禁
- 新增 scripts/dev/verify_convert_seed.py(pymysql 等价 reset 流程 + 8 条 DoD 断言,
  含断言 ⑧「费率档 ↔ product_type 匹配」,越档即 FAIL)

T-2 / T-2b(纯函数包 + 示例实算回填)
- 新增 app/service/convert/ 7 文件:__init__ / types / calc / fee / nav / lot_bootstrap / errors
  (纯函数,不查库、不碰 SQL;所有量化显式 ROUND_HALF_UP;lot_bootstrap 用 zlib.crc32
   保证 D18 跨进程同源)
- 新增 tests/test_convert_calc.py 93 用例(12 类:HALF_UP 反向自证 / 分档边界 /
  FIFO 含同 confirmed_at 兜底 / 双口径 / 强制全转与强制赎回 / PRD §5.3 全链自证 /
  纯函数零 IO 依赖断言)
- 重写 scripts/dev/calc_convert_demo.py:去掉脚本内公式副本,改为调用生产 calc.py,
  末尾与 PRD §5.3 逐项比对(不一致即退出码 1),兼作一致性门禁

验证
- pytest 609 passed / 3 skipped(516 → +93,零回归)
- verify_convert_seed.py 8/8 PASS;calc_convert_demo.py 15/15 与 PRD §5.3 一致

文档:PRD v0.9.1(费率分类修正)· 架构 §7 签名回填 / §8.3 错误码注 / §15 T-2 完成 ·
开发计划 §1.5 新增 R-h + §4.2·§4.3 执行记录 · AGENTS.md · docs/memory
2026-09-10 14:45:55 +08:00
GaoYiYuan_0626 ea9a132a40 docs: 基金转换(convert)设计阶段成果入库
此前 AIcoding 第 1~3 步的产出仅存在于本地磁盘、未纳入版本库,本次一次性入库,
补齐「设计可追溯」。本提交为纯文档 + 一个实算脚本,**代码零改动**,接口契约与表结构不变。

入库 6 个文件:

- docs/PRD/PRD-基金转换交易.md(v0.9 · 需求权威:FR-C1~C16 / §4 表结构 / §5 接口 / §9 验收)
- docs/项目框架设计/架构设计-基金转换交易.md(v1.0 · 实现依据:D1~D20 / §5 事务补偿 /
  §11.1 DB 账号分离 / §15 任务映射 T-0~T-13 与 §15.1 依赖拓扑)
- docs/项目框架设计/基金转换-审查意见处置表.md(33 条外审处置 + §九 用户拍板记录)
- docs/项目框架设计/评审待办-风控主架构与基金转换.md(评审逐条判定 + 核实证据)
- docs/交接文档-基金转换.md(本线交接入口,读完即可开工)
- scripts/dev/calc_convert_demo.py(PRD §5.3 主示例实算回填脚本——派生值禁手算)

门控:PRD 已定稿并经独立评审闭环(M-7 已满足);架构 v1.0 独立评审 13 条建议 0 悬空。
下一步:AIcoding 第 4 步「产出开发计划」,输入为架构 §15 / §15.1。
2026-09-10 11:29:55 +08:00
GaoYiYuan_0626 66ec7eba08 feat: C6 FR-10 代理人行为链 RISK-008
代理人异常行为链识别(RISK-008)落地:
- 新增 agent_behavior_service:三条件证据聚合(A 诱导调仓/B AUTH_403_SCOPE 越权试探/C AUTH_403_NOT_OWNER|NOT_ASSIGNED 越权查询),按代理人维度独立出 pattern 单,payload.actor_id 指向代理人,审计仅 INSERT event_type=agent_behavior_detected。
- risk_repository 新增 list_audit_events / find_agent_behavior_alert / merge_agent_behavior_payload(同日同代理人一张单,证据并集)。
- trade_gateway.submit_trade 补 actor_id 透传(代理人发起交易归属发起人,缺省 SYSTEM);simulate 路由传入 auth.actor_id。
- chat_tools 新增 query_agent_behavior 只读 Tool(agent_id 过滤 + 客户脱敏),tool_service 补意图词与摘要。
- scripts/cron/agent_behavior_scan.py 定时扫描脚本。
- 修复 append_alert_event 序列化缺 default=str(C6 evidence 含 datetime 字段)。
- 单测 12 例(_count_induce 边界 / 三条件 / 出单去重 / payload 归属 / Tool 过滤脱敏)。
全量 pytest 482 passed 0 failed(470 基线 + 12 C6)。
2026-09-07 19:53:39 +08:00
GaoYiYuan_0626 90d0660b44 feat: C5 FR-9 预警处置时效升级(RISK-007,P0)
- 仓储:新增 list_pending_alerts_all / update_alert_escalation(统一读改写 payload,
  升级标记独占写入,status/handler_* 列不碰;_parse_alert 补 created_at/handled_at
  字符串→datetime 解析,兼容 sqlite 原生 DDL)
- escalation_service.scan_and_escalate:扫描判级(普通 4h/24h、AML 1h/4h 短通道)、
  幂等闸门(仅升不降)、先持久化再推送、按 (customer_id,level) 降噪合并一次推送、
  通知链累积(L1 含 risk_manager / L2 含 compliance)、逐单+任务级审计
- scripts/cron/escalation_scan.py:15min 定时扫描壳(sys.path 引导 + new_trace + JSON 摘要)
- 对话线 query_overdue_alerts Tool + 注册表 + 意图词(置于 alert_query 之前)+ summarize 分支
- C5 前置:seed STAFF-31001/31002(risk_manager) + deps 矩阵放行 + chat.py 显式 deny +
  risk.py 台账全量只读分支;JWT 手册 §5.3/§5.4/§6.1 增补 risk_manager
- 测试:conftest 回拨 fixture + test_escalation_service(9) + risk_api/manager(5) +
  chat deny(1) + chat_tools overdue(2);全量 470 绿(453+17)
2026-09-07 19:28:04 +08:00
GaoYiYuan_0626 b5fd52ef02 AL-08: 演示库重灌与种子核对对齐 main 基准——prepare_risk_demo.sql 改写 expires_at 口径(演示客户刷新为测评 90 天前/剩余 275 天,CUST-1004 保持过期演示 FM-03,替代 SUIT-008 evaluated_at+365 旧写法);演示 SOP 同步(§2 完成标志 33 客户口径+expires_at 剩余天数,A-1 预期改 SUIT_RISK_MISMATCH,A-2 改 FM-01 SUIT_AGE_CONFIRM 网点确认);网关阻断响应补全 main 契约字段(match_result/mismatch_type/requires_disclosure/needs_branch_confirm/rule_refs)+audit detail 加 block_response_code;集成测试 4 例旧口径断言更新(A-1 trace 一致性断机器码/A-1 audit JSON 断机器码/A-2 重写为 FM-01 语义/settings 退役断言)。SOP §2 脚本化全量重灌双库验证通过(AML 8 条/演示 7 行/sync 33 rows);全量 406 passed 0 failed 0 skipped(集成测试真库走 21 列新表+expires_at 数据恢复执行) 2026-09-07 17:49:39 +08:00
GaoYiYuan_0626 bb244f43a3 AL-01: Core 库表结构对齐 main 基准——01-ddl 采纳 main 版(新增 core_suitability_rule C×R 矩阵表+FK/core_customer KYC 扩列含 is_hnw/core_customer_risk 七新列含 expires_at+investor_category/core_product 起购金额/期限/揭示书列/core_trade AML 扩列/core_cash_flow flow_subtype);02~06 种子采纳 main 版(33 客户含 CUST-DEMO-A~E/14 产品/矩阵 25 行);保留我方独有 STAFF-90001 风控演示账号;README 种子规模与矩阵口径同步。非交互重灌验证通过(矩阵 25/客户 33/产品 14/风评 33) 2026-09-07 16:57:15 +08:00
GaoYiYuan_0626 535c8a0a20 fix: T21 真库联调三坑——①Milvus Lite 文件库跨进程重开 collection released, ensure_collection 补显式 load(单测同进程覆盖不到); ②faiss 不支持中文路径(faiss C++ IO), 本机 .env 改英文绝对路径 C:/Users/YUAN/.jinrong/milvus/, .env.example 加警告; ③build_kb 直跑 sys.path 引导项目根(同 rebuild_alerts 先例); 真库 24 块入库+检索验收通过(基金申购 Top3 命中申赎规则, 溯源完整), 425 绿 2026-09-07 12:11:10 +08:00
GaoYiYuan_0626 7edf522aff feat: T21-3 知识库首批数据+入库脚本——data/kb 6 只种子产品手册(每只 prospectus/fee/rule/risk 四节 24 块, 费率与 core_product seed 对齐)+build_kb.py(front-matter+节标题切块/doc_type 白名单/Core 权威元数据缺省即中止/一次批量 embedding/upsert 幂等/--dry-run); 脚本单测 10 例, 404 绿 2026-09-07 11:55:24 +08:00
GaoYiYuan_0626 61711cb70a fix: Wave 0 独立 AI 评审闭环(有条件通过→闭环, 273 绿)——P2-1 python-jose>=3.5.0(防 CVE-2024-33663/33664) P2-2 audit 降级+OPTIONS 测试 P2-3 JWT 通道 chat 3 例 P2-4 debug 兜底双闸门(dev+无 RS256 公钥) P2-5 留痕失败降级显式化(401/403 拒绝语义不漂移, _safe_unauth_audit 保护 repo 构造); P3 顺手: 401 归 platform/值域外 400 补留痕/Bearer 大小写不敏感/jti 加 uuid/CLI permissions+exp 上限/dev 默认密钥 warning/LLM 单例加锁/chat 主角色优先序+message strip+guard 日志脱敏/必填 claims 参数化+none 算法固化; P3 登记项(会话枚举/seq_no 锁/trace_id 口径/文档同步)归 M4 文档 2026-09-07 03:03:12 +08:00
GaoYiYuan_0626 36f476f450 feat: T-01 JWT 鉴权——auth_service(Auth SDK: HS256/RS256 验签+必填claims显式校验+jti吊销 fail-open) + deps 工厂替换(Bearer 全环境, debug 头降级 dev 兜底) + X-Agent-Type 交叉校验(手册§4.6/§5.4 准入矩阵) + issue_dev_token CLI + redis_gateway exists/set_ex + lifespan 改验 jwt_ready + tests/test_auth_jwt 18 例, 228 绿 2026-09-07 02:23:33 +08:00
GaoYiYuan_0626 e62fb1d291 fix: B9a 复审闭环——P2-1 skipped 分支 has_engine_error_audit 中间态警示(input_summary LIKE)+docstring 局限声明; P2-2 非首笔 trade 幂等用例; P3 顺手: 双脚本连接自检/ImportError 分支/非 dict payload raw 兜底/get_trade_by_id confirmed 过滤/勿并行声明, 8 例 204 绿, warning 路径真库复验后清理; docs 同步复审结论 2026-09-07 01:02:12 +08:00
GaoYiYuan_0626 fdf487dc32 feat: B9a 演示/运维脚本——subscribe_alerts(risk:pub:alert 订阅演示/连接自检/--duration) + rebuild_alerts(按 trade_id 幂等重放: find_alerts_by_trade payload LIKE 查已入单防重复出单与重复 append, 含 aml/已处置单) + core_ro.get_trade_by_id 只读扩展 + test_demo_scripts 5 例, 201 绿; 真库手工验证(出单→幂等 skip→missing exit1; 订阅端到端 trace 贯通)后现场清理; docs 同步(TODO/MEMORY/FLOW/开发计划) 2026-09-07 00:43:25 +08:00
GaoYiYuan_0626 03f8b63780 fix: FR-4 payload 上下文补组装/种子名单唯一命中/规则流水升序等(B4 评审 P1-1、P2-2~5、P3×7) 2026-09-06 17:40:13 +08:00
GaoYiYuan_0626 487b9da5b1 docs: 风控模块 PRD v1.0/规则附表/架构 v1.1/开发计划 v1.1 + 文档联动修正 2026-09-06 15:09:13 +08:00
zhanghongyu_0626 1ddd44a6cb Add core database support and enhance documentation
- Introduced `MYSQL_CORE_DATABASE` in `.env.example` and `settings.py` for core database configuration.
- Added `CoreReadOnlyRepository` for read-only access to the `jinrong_core` database.
- Updated `AGENTS.md`, `README.md`, and various documentation files to reflect new agent onboarding processes and project structure.
- Revised requirements in `requirements.txt` to include `langgraph` and `langchain-core`.
- Enhanced `FLOW.md` with local bootstrap instructions for setting up the core simulation environment.
- Added new scripts for database creation and seeding for the core simulation library.
- Improved overall documentation for clarity on project architecture and memory management.
- Updated `TODO.md` to reflect current development priorities and tasks.
2026-09-05 17:39:16 +08:00