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
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# 2026-09-10 工作日志
|
||||
|
||||
> 本日两条线:**架构改进线收尾闭环**(本文主体)+ 基金转换线**第 4 步开发计划待启动**。
|
||||
|
||||
---
|
||||
|
||||
## 一、接手与状态核对(会话起始)
|
||||
|
||||
读入:`docs/交接文档-基金转换.md`(v1.0)· `docs/memory/MEMORY.md §0` · `docs/交接文档-架构改进.md`(v1.1)
|
||||
· `docs/项目框架设计/架构设计-基金转换交易.md §15/§15.1` · `docs/memory/{TODO,ITERATION}.md` · `AGENTS.md`。
|
||||
|
||||
**基线复核**:`python -m pytest -q`(系统 Python 3.13.14)→ **510 passed**(9.93s)。
|
||||
|
||||
**设计资产核对**:`tests/_ddl.py:50-54` 的 `core_holding` 仍为 `market_value`/`quantity` 且**无 PK** ——
|
||||
与 T-0 描述**一致**,架构文档无误。
|
||||
|
||||
### ⚠️ 核对中发现的两处「文档与实况不符」(以实况为准)
|
||||
|
||||
1. **`037ce7e`「未 push」是过期表述。**
|
||||
实测 `git ls-remote origin`:`refs/heads/risk-control-agent` = **`fffb78a`** = 本地 HEAD;
|
||||
`git merge-base --is-ancestor 037ce7e fffb78a` → 成立。即**架构改进线全部提交(含其后 5 个文档提交)早已推送**。
|
||||
本地 `git branch -vv` 显示 `origin/risk-control-agent: gone` **只是本地远程跟踪引用失效**,非远程分支被删。
|
||||
2. **基金转换线 6 个设计文件全部 untracked**(未纳入版本库):`docs/PRD/PRD-基金转换交易.md`、
|
||||
`docs/项目框架设计/架构设计-基金转换交易.md`、`基金转换-审查意见处置表.md`、
|
||||
`评审待办-风控主架构与基金转换.md`、`docs/交接文档-基金转换.md`、`scripts/dev/calc_convert_demo.py`。
|
||||
→ 用户拍板:**先 commit 入库(只 commit,不 push)**。
|
||||
|
||||
---
|
||||
|
||||
## 二、架构改进线 §7.2 手工冒烟(七项,全部代跑,2026-09-10)
|
||||
|
||||
用户授权「七项全部代跑」。所有临时改动**跑完即恢复**。
|
||||
|
||||
### 第 1 项 · 无 key 启动告警 ✅ PASS
|
||||
|
||||
- 本机 `.env` 的 `DEEPSEEK_API_KEY` **本就为空**(实测 `raw_value_len = 0`),故「清空」态即当前态。
|
||||
- `uvicorn app.main:app`(端口 8123)→ **1 秒就绪**,`/health` → `200 {"status":"ok","env":"development"}`。
|
||||
- 进程存活 → **告警不阻塞启动** ✓。
|
||||
- 日志命中:`DEEPSEEK_API_KEY 未配置,对话将走降级回复(前缀 (LLM 未配置:请在 .env 设置 DEEPSEEK_API_KEY 后重启)),LLM 能力不可用`(`app/main.py:66-72`)。
|
||||
|
||||
### 第 2 项 · 恢复 key → 无告警 ✅ PASS(**口径替代,已留痕**)
|
||||
|
||||
- **替代说明**:本机无真实 DeepSeek key,「恢复 key」一支改用**非空占位值**(`sk-smoke-placeholder-not-a-real-key`)。
|
||||
告警分支只判 `not settings.deepseek_api_key`,**不校验 key 有效性**,故该替代足以验证条件分支。
|
||||
- 改后 `key_present: True` → 启动(端口 8124)→ `/health` 200 → 日志**未出现**该告警 ✓。
|
||||
- 顺带验证 trace 中间件:响应头 `x-trace-id: trc-48349abc526e4055` / `x-request-id: req-612a3f2acb8349d6` ✓。
|
||||
- **`.env` 已还原**,`key_present: False`(原状)✓。
|
||||
|
||||
### 第 3 项 · 交易阻断 + 放行各一次 ✅ PASS
|
||||
|
||||
| 场景 | 请求(debug 头 `X-Debug-Role: risk_demo` / `X-Debug-Actor: STAFF-DEMO`) | 响应 |
|
||||
| --- | --- | --- |
|
||||
| 阻断(SOP A-2) | CUST-4001 / PROD-161725 / subscribe / 20000 | `blocked=true`,`block_response_code=SUIT_AGE_CONFIRM`,`needs_branch_confirm=true`,`rule_refs=["FM-01"]` |
|
||||
| 放行(SOP A-3) | CUST-3001 / PROD-510300 / subscribe / 500000 | `blocked=false`,`triggered_rules=["RISK-001","RISK-002"]`,`alert_ids=["ALT-20260910-7BCBBC94"]` |
|
||||
|
||||
**与 SOP 记录的预期逐字一致。** 库内核验:
|
||||
|
||||
- `core_trade`:阻断单 `TRD-20260910-C1D8AA26` **未落库** ✓;放行单 `TRD-20260910-FE545DF0` 已落库(`confirmed`, 500000.00)✓
|
||||
- `risk_suitability_log`:CUST-4001 `is_blocked=1` ✓;`risk_alert` 出 `suitability` 单 `ALT-20260910-652280E1`(`pending_review`, score 90)✓
|
||||
- `audit_log`:`suitability_block / suitability_alert_created`、`trade_request / suitability_blocked`(`rule_id=SUIT_AGE_CONFIRM`)、
|
||||
`trade_request / trade_accepted`(放行)、`risk_judgement / alert_created` ✓
|
||||
|
||||
### 第 4 项 · 预警聚合行为不变 ✅ PASS
|
||||
|
||||
SOP A-4:CUST-9527 / PROD-510300 / subscribe / 1000 **连发 4 笔**:
|
||||
|
||||
| 笔次 | `triggered_rules` | `alert_ids` |
|
||||
| --- | --- | --- |
|
||||
| 1 | `[]` | `[]` |
|
||||
| 2 | `[]` | `[]` |
|
||||
| 3 | `["RISK-003"]` | `["ALT-20260910-C8600425"]` |
|
||||
| 4 | `["RISK-003"]` | `["ALT-20260910-C8600425"]` ← **与第 3 笔同一张单(并入,非新建)** |
|
||||
|
||||
库内核验:
|
||||
|
||||
- `risk_alert` CUST-9527 当日 **`pending_review` 单数 = 1** ✓;`payload.events` **长度 = 2** ✓
|
||||
- `audit_log` 呈 **`alert_created`(1184) → `alert_appended`(1187)`** 序列,聚合语义在审计层可证 ✓
|
||||
- 4 笔交易全部落库(`TRD-20260910-B66CB0D8` / `B39E1456` / `83D58A41` / `79EDC16E`)✓
|
||||
|
||||
> 注:`risk_alert.status` 实际取值为 **`pending_review`**(文档口头简称「pending」)。首次核库我按 `status='pending'` 查得 0 条,
|
||||
> 系**断言写错**而非系统问题,修正后为 1 ✓。
|
||||
|
||||
### 第 5 项 · Redis 不可用 → 退回进程内锁 ✅ PASS(**口径替代,已留痕**)
|
||||
|
||||
- **替代说明**:本机 Redis 为 `redis-server.exe`(PID 7716,Services 会话),**无文档化启动方式、`redis-server` 不在 PATH、
|
||||
`sc.exe` 被本机安全策略拉黑**,杀掉后无法保证原样拉起(且 `FLOW.md` 明示其非权威数据源)。
|
||||
改用 **`REDIS_URL=redis://127.0.0.1:6399/0`(死端口)**:应用侧抛 `ConnectionError`,与真宕机**同一条代码路径**
|
||||
(`redis_gateway._ensure()` → 命令异常 → `locks._acquire_redis` 的 `except Exception` → `unavailable` → 进程内锁),且完全可逆。
|
||||
- 结果:启动**不被阻塞**(1s,`/health` 200)→ 交易返回 `blocked=false`,`alert_ids=["ALT-20260910-7BCBBC94"]`,**业务照常完成** ✓
|
||||
- 日志**明确出现 2 次** `Redis 锁不可用,退回进程内锁`:
|
||||
`lock:agg:event:CUST-3001:2026-09-10`(`alert_service` 聚合锁)、`lock:l3:CUST-3001`(`profile_l3` L3 锁)✓
|
||||
- 底层异常:`redis.exceptions.ConnectionError: Error 10061 connecting to 127.0.0.1:6399`,被正确吞噬未上抛 ✓
|
||||
|
||||
### 第 6 项 · Redis 恢复 → 行为一致 ✅ PASS
|
||||
|
||||
- `redis.Redis.from_url(settings.redis_url).ping()` → **True**(`redis://127.0.0.1:6379/0`)。
|
||||
- 同笔交易 → `blocked=false`,`alert_ids=["ALT-20260910-7BCBBC94"]`(并入同单)✓
|
||||
- 日志中 `退回进程内锁` **命中 0 行** → 确实走 Redis 锁路径,**未降级** ✓
|
||||
- `triggered_rules` 由 `["RISK-001","RISK-002"]` 变为 `+RISK-003`:CUST-3001 当日累计至第 3 笔触发频次规则,
|
||||
**属预期累积行为,非不一致**。
|
||||
|
||||
### 第 7 项 · 调换装饰器验证 T-202 守卫变红 ✅ PASS(本项价值最高)
|
||||
|
||||
守用例:`tests/test_audit_middleware.py::test_audit_middleware_runs_inside_trace_middleware:210`。
|
||||
|
||||
| 步骤 | 结果 |
|
||||
| --- | --- |
|
||||
| ① 基线 | `1 passed`(绿) |
|
||||
| ② 调换 `main.py` 两装饰器(trace 先注册 → 变最内层;audit 后注册 → 变最外层) | 已调换,行序:`trace_middleware`@89 / `audit_middleware_entry`@117 |
|
||||
| ③ 跑守卫 | **`1 failed`** ✓ 变红 |
|
||||
| ④ 失败断言原文 | `AssertionError: audit 若先于 trace 执行,此处会静默为空` / `assert ''` @ `test_audit_middleware.py:221` |
|
||||
| ⑤ 还原 | `sha256` 前后同为 `d9d7559445dffd2a4576cdc5454030d3acae5fd4c076de8daa5a35f53802510a` → **字节级一致** ✓ |
|
||||
| ⑥ 再跑守卫 | `1 passed`(转绿)✓ |
|
||||
| ⑦ 现场核对 | `git status --short app/main.py` 空、无残留 `*.smoke.bak` ✓ |
|
||||
|
||||
→ **守卫确实能抓住「audit 先于 trace 执行 → 全站审计静默丢 trace_id 且不报错」这一回归**。设计意图达成。
|
||||
|
||||
---
|
||||
|
||||
## 三、冒烟副产物与现场恢复
|
||||
|
||||
### 3.1 集成测试失败(防护行为,非回归)
|
||||
|
||||
冒烟后在真 MySQL 上跑全量:**507 passed / 3 failed**,全部落在 `tests/test_integration_risk.py`:
|
||||
|
||||
- `_assert_a3_clean` → `CUST-3001 今日已有 confirmed 交易(影响 RISK-002 累计/RISK-003 频次口径)`(我的 `TRD-20260910-FE545DF0`)
|
||||
- `_assert_a4_clean` → `CUST-9527 今日已有 confirmed 交易(A-4 频次计数失真)`(我的 `TRD-20260910-B66CB0D8`)
|
||||
- `test_a7_handle_state_machine_compliance_forbidden` → 级联 `KeyError: 'a3_alert_id'`
|
||||
|
||||
→ 这正是**核查单⑥「演示/测试同日交叉防护」前置断言**在按设计工作(交接文档已预警)。
|
||||
|
||||
### 3.2 演示库重灌(用户拍板:现在重灌)
|
||||
|
||||
按 `docs/项目框架设计/演示SOP-风控模块.md §2` 脚本化方式执行(`MYSQL_PWD` 传密码,避开 `reset.ps1` 交互 `-p` 卡死):
|
||||
|
||||
1. `DROP DATABASE IF EXISTS jinrong_core; DROP DATABASE IF EXISTS jinrong_agent;`
|
||||
2. 依序灌 11 个 SQL:`scripts/core/00~06` → `表设计/01-mysql-共用底座.sql` → `表设计/02-mysql-agent专用.sql`
|
||||
→ `scripts/agent/seed-aml-list.sql` → `scripts/demo/prepare_risk_demo.sql`
|
||||
3. `python scripts/sync/sync_advisor_rel.py`
|
||||
|
||||
**完成标志三项全部吻合**:AML 名单 **8** 条 · `prepare_risk_demo` **7** 行测评(`expires_at` 剩余 **275** 天)·
|
||||
`sync_advisor_rel` upserted **33** rows。
|
||||
|
||||
### 3.3 全量复跑(用户要求「改完后再跑一次」)
|
||||
|
||||
**`510 passed, 656 warnings in 9.52s`** → **复绿** ✓
|
||||
|
||||
现场状态:`.env` 已还原(key 为空、`REDIS_URL` 为 6379)✓ · `app/main.py` 字节级还原且 git 无改动 ✓ · 无残留 `.smoke.bak` ✓。
|
||||
|
||||
---
|
||||
|
||||
## 四、回写清单
|
||||
|
||||
| 文档 | 改动 |
|
||||
| --- | --- |
|
||||
| `docs/项目框架设计/TODO-架构改进.md` | T-107 手工冒烟勾选 + 完成判定块三勾 + 状态块改「全部闭环」+ push 状态纠正 |
|
||||
| `docs/交接文档-架构改进.md` | **v1.1 → v1.2**;状态块重写(push 纠正 + 两处替代口径留痕);§7.3 全勾;§10 重写为结项 |
|
||||
| `docs/交接文档-基金转换.md` | §9「另一条线的遗留」改为**已闭环**,纠正 push 表述 |
|
||||
| `docs/memory/MEMORY.md` | §0 末尾「待办两条线」改为「架构改进线已结项」+ push 纠正常见坑 |
|
||||
| `docs/memory/TODO.md` | 模块侧开放项「架构改进线 · 收尾」勾选并附七项证据摘要 |
|
||||
| `docs/memory/ITERATION.md` | 新增 **2026-09-10 架构改进线收尾闭环** 迭代行 |
|
||||
| `docs/memory/2026-09-10.md` | **本文件(新建)** |
|
||||
|
||||
---
|
||||
|
||||
## 五、下一步
|
||||
|
||||
**架构改进线:结项,无待办。**(可选:push 后打里程碑 tag,待用户拍板)
|
||||
|
||||
**基金转换线:第 4 步「产出开发计划」** —— 输入已齐备:
|
||||
`docs/项目框架设计/架构设计-基金转换交易.md` §15(任务映射 T-0~T-13)+ §15.1(依赖拓扑与并行分组)+ §12(风险表);
|
||||
产出落点 `docs/项目框架设计/开发计划-基金转换交易.md`(待创建)。
|
||||
@@ -39,6 +39,7 @@
|
||||
| 代理人助手 Agent | L2 画像、RAG、草稿 | L1 只读、Milvus | 空壳 service(chat 骨架已通) |
|
||||
| 数据分析 Agent | NL→SQL→解读 | Core RO、画像只读 | 空壳 service(chat 骨架已通) |
|
||||
| 风控监测 Agent | 预警、L3、R-02 适当性 | 交易事件、AML 名单 | **已实现 B1~B7 + C1~C3(对话线四 Tool + risk 分支 StateGraph,A-6 验收)**;追加 FR-8/9/10(C4~C6:集中度/时效升级/行为链)方案已定稿待编码 |
|
||||
| **基金转换(convert)交易** | 转出赎回 + 转入申购、逐批次计费、补差费、批次维护 | Core 写(仅 `app/gateway/`)、`core_share_lot` 批次表、规则引擎 | 🟡 **第 5 步进行中**(**T-0 / T-0b / T-1 / T-2 / T-2b 已完成(609 passed),下一步 = T-3**):PRD **v0.9.1** + 架构 **v1.0** + 开发计划 **v1.0**(T-3~T-13 待实现);入口 `docs/交接文档-基金转换.md` |
|
||||
| Core 只读层 | L0 事实查询 | `jinrong_core` | **已实现 + 已接对话 Tool(T-04)**:core_ro 经 app/tool/core_tools.py 三只读 Tool(L0/持仓/流水)进 chat;风控扩展查询照旧 |
|
||||
| 共用底座 | 会话、审计、输入防护 | MySQL 11 表 + Redis | **已接入(2026-09-07)**:会话(T-06 session_repository + memory_service 窗口)、审计中间件(T-02 http_access + input_guard_log 双写)、agent_tool_call Tool 留痕(T-04)、输入防护(T-03 input_guard:注入词表纯函数检测 + oversize + Redis 固定窗口限流,chat 链路 限流→注入/超长→归属) |
|
||||
| 对话编排 | LangGraph StateGraph + DeepSeek | langgraph/langchain-openai | **已实现(T-07 骨架 + T-04 Tool 节点 + C1 风控四 Tool + C5 追加 query_overdue_alerts 共五只 + C2 risk 分支)**:tool(分组关键词意图→Tool,customer/advisor/risk)→llm→guard(免责声明);analyst 分支与 LLM intent 待后续 |
|
||||
|
||||
@@ -12,3 +12,11 @@
|
||||
| 2026-09-07 | 《实现方案-风控追加需求v1.1-C4C6.md》定稿:独立 AI 评审 FAIL(P0×1/P1×4/P2×6)→ 修订闭环;C4~C6 编码依据可照抄 | 开发流程第 2 步(另一 AI 验证挑毛病) | 实现方案文档 |
|
||||
| 2026-09-07 | 全库文档同步:分支口径修正(`feature/risk` → 实况 `risk-control-agent`,与 main 分叉 67/4)+ T-21 评审闭环 / 425 绿 / T-03 / C1~C3 状态落账 + 新建项目根交接文档.md | 文档与实况不一致(用户排查要求) | MEMORY / TODO / REQUIREMENTS / FRAMEWORK / FLOW / ENVIRONMENT / ITERATION / PRD / 架构设计 / 开发计划 / README / 交接文档 |
|
||||
| 2026-09-07 | **阶段一「对齐 main 基准」AL-01~AL-08 完成**(逐项独立 commit bb244f4~b5fd52e):SUIT-001~008 换核为 core_ro.check_suitability(C×R 矩阵 + match_result 五值 + JR-AST/FM 契约)· risk_suitability_log 重建 21 列 · Core 表 is_hnw/风评 expires_at 七新列 · 种子 33 客户/14 产品 · 全量 **406 passed 0 failed 0 skipped**(425→406,SUIT 测试重写)· uvicorn 冒烟三端点通过 · risk-m1 补打(3c07de6)· 六项拍板全落定 | 阶段一执行(用户拍板 main = 需求唯一基准);基线变更与 SUIT 体系退役需落账 | MEMORY / TODO / REQUIREMENTS / FRAMEWORK / FLOW / ENVIRONMENT / ITERATION / README / 交接文档 |
|
||||
| 2026-09-08 | 前端接入面交付:**方案 B** 会话管理三端点(`8328c24`)+ **方案 C** SSE 流式对话(`01ec5fc`),测试 482→**503 绿** | 前端 React 多 Agent 入口需后端接入面先就绪(用户拍板方案 B+C) | chat.py / session_repository / agent_service / 测试 / README / 交接文档 |
|
||||
| 2026-09-09 | **架构改进与稳定性加固 T-101~T-202**:Redis 双层分布式锁(T-201)+ 无 key 启动告警(T-107)+ 审计 trace 顺序守卫(T-202)+ 文档口径勘误(词表 45 / Tool 5 / Agent 表 6);pytest 503→**510 绿**(+7);接口契约与表结构零变更 | 架构梳理发现 24 项待处理,两轮独立审核通过;用户拍板「补告警 + 换 Redis 锁」;合并 main 前只做低风险项 | 交接文档-架构改进 / 架构设计-风控模块 / MEMORY / 开发计划-架构改进 / TODO / 测试 +7 |
|
||||
| 2026-09-10 | **架构改进线收尾闭环**:§7.2 七项手工冒烟补跑 **7/7 PASS**(无 key 告警 / 交易阻断放行 / 预警聚合 / Redis 降级退回进程内锁 / Redis 恢复零降级 / 调换装饰器使 T-202 守卫变红后字节级还原);**核实并纠正「`037ce7e` 未 push」的过期表述**(远程 `risk-control-agent` = `fffb78a` = 本地 HEAD,早已推送);冒烟残留按 SOP §2 重灌双库清除,全量 **510 passed** 复绿 | 架构改进线唯一缺口是手工冒烟未执行;交接文档/项目记忆的 push 状态与实际 git 状态不符(以 `git ls-remote` 为准) | TODO-架构改进 / 交接文档-架构改进 / 交接文档-基金转换 / MEMORY / TODO / ITERATION / 当日日志 |
|
||||
| 2026-09-10 | **基金转换(convert)线设计闭环**:PRD **v0.9**(33 条外审闭环)+ 架构 **v1.0**(独立评审通过,13 条建议 **0 悬空**)+ 执行期风险 5 条 + **D18/D19/D20**;**DB 账号分离定案**(用户拍板「按真实项目走」,接主架构 C1);**新建 `docs/交接文档-基金转换.md`** | `PRD-风控监测Agent` FR-1 一期显式拒收 convert,需放开;合规口径须外部事实核验(证监会公告〔2025〕22 号);账号方案为跨线交叉点 | PRD-基金转换 / 架构设计-基金转换 / 评审待办 / 处置表 / **交接文档-基金转换(新)** / MEMORY / TODO / 交接文档.md / 交接文档-架构改进 / 开发计划-架构改进 |
|
||||
| 2026-09-10 | **基金转换(convert)线第 4 步开发计划闭环**:产出 `docs/项目框架设计/开发计划-基金转换交易.md`(**v1.0,1,047 行**,T-0~T-13 逐任务改法 + DoD + §0 速览 + §1.4 **十五条代码事实核对表** + §1.5 **七条实现级裁定 R-a~R-g** + §12 **十六条回归面** + §13 PRD 19 条验收映射 + §14 实现级风险表);**经四轮独立子代理审核收敛:12 条 → 接受 11 / 驳回 1(附实测证据)/ 0 悬空**(一轮补 R15 真 MySQL 集成用例 `test_integration_risk.py:426-437` + R16 redeem 用例 + **R-c 拆「降级规则 + 覆盖率补偿」双条** + F-11 计数 11→13;二轮抓出 **R-a 并发论证不成立**——执行权锁是**请求粒度** → 增第 ③ 步 `IntegrityError` 回退 + T-6 并发用例;三轮修 §14「四道→五道」口径不一致 + **实测驳回**「SQLAlchemy 会抛 `PendingRollbackError`」的推测;四轮终验通过、无「声明已改实际未改」残留);**三项实测加固**(sqlite 3.50.4 不支持 `ON DUPLICATE KEY UPDATE`,且 `WHERE NOT EXISTS`/`FROM DUAL` 两个候选写法**方言互斥**;SQLAlchemy 2.0.51 异常后同事务可用;pydantic 2.13.4 `Optional+gt` 保 422) | AIcoding 第 4 步;用户要求「写完拉起子进程审核,改完后再审,审到没问题才能进行下一步」;架构/PRD 存在 2 处口径不一致需裁定(W-1/W-2),且实现级空白 7 处需收口 | 开发计划-基金转换交易(新) / 交接文档-基金转换(v1.2) / MEMORY / TODO / ITERATION / skill `ai-coding-step4-dev-plan`(新) |
|
||||
| 2026-09-10 | **基金转换线:T-0 / T-0b / T-1 落地 + PRD 费率分类修正(v0.9 → v0.9.1)**:**T-0** sqlite/MySQL 结构对齐(`core_holding` 列名+PK、补 `core_product_nav`)+ 建库自校验 · **T-0b** DB 账号分离 D20(`xh_core_ro` / `xh_core_rw` / `xh_agent_rw`)· **T-1** 数据层(`core_fee_rule`、`core_share_lot`、`core_convert_lot_detail`、`risk_convert_detail` 四表 + `07-seed-fee-rule` / `08-seed-share-lot` / `09-seed-org` 三个种子 + reset 链),全量 **516 passed / 3 skipped**(批 0 后基线,零回归),T-1 数据层断言 **8/8 PASS**;**外审漏网的费率分类错误修正**——`PROD-005827` 由「主动偏股 0.0080」归位「其他混合型 0.0050」(22 号文 §8 分档),主示例转入方改真主动偏股 `PROD-003095`,`09-seed-org` 按真实「管理人全产品线」重排为 v1.1 | 用户驳回「那是两种类型,怎么能混呢?」并强调「按真实业务走,强调很多遍了,这个项目要贴近真实,是要上线的」;根因是**五轮评审只做内部自洽核对,从未做「产品类型 ↔ 费率档」匹配核对** | PRD-基金转换(v0.9.1) / 开发计划-基金转换交易 / 交接文档-基金转换(v1.3) / 架构设计-基金转换交易 / AGENTS.md / `09-seed-org.sql`(v1.1) / `verify_convert_seed.py` / MEMORY(自检第 11、12 问) / TODO / FRAMEWORK |
|
||||
| 2026-09-10 | **基金转换线 T-2(纯函数包)+ T-2b(实算回填)完成**:新增 `app/service/convert/` **7 文件**(`__init__` / `types`(frozen dataclass + 3 归一工具)/ `calc` / `fee` / `nav` / `lot_bootstrap` / `errors`(`ConvertError` + **11** 子类))+ 新增 `tests/test_convert_calc.py` **93 用例**(12 类,含 **HALF_UP 反向自证**与**纯函数零 IO 依赖断言**,把 DoD 的「人工 grep」升级为硬断言);**重写 `scripts/dev/calc_convert_demo.py`** —— 去掉脚本内公式副本、改调生产 `calc.py`、末尾 15 项自校验(不一致即退出码 1),使其兼作「PRD §5.3 ↔ 生产口径」一致性门禁;新增实现级裁定 **R-h**(`plan_lots` 零剩余不触发强制处置);两处文档计数差异已留痕(异常实为 11 个而非 8 个 / 架构 §7 补 `hold_days`·`rounding_diff`·`ensure_batch_limit` 三签名);全量 **609 passed / 3 skipped**(516 → +93,零回归) | 用户「继续推进到结束」;T-2 是并行组 A(T-3/T-4/T-5)与 T-2b 的唯一前置;原实算脚本自带公式副本 = 与 D18 同类的漂移风险 | 开发计划-基金转换交易(§1.5 R-h + §4.2/§4.3 执行记录 + DoD 勾选) / 架构设计-基金转换交易(§7 签名回填 · §8.3 `FEE_RULE_MISSING` 注 · §15 T-2 完成) / PRD-基金转换(§5.3 数值来源) / 交接文档-基金转换(v1.4) / AGENTS.md / `app/service/convert/*`(新) / `tests/test_convert_calc.py`(新) / `scripts/dev/calc_convert_demo.py` / MEMORY / TODO / FRAMEWORK / ITERATION |
|
||||
| 2026-09-10 | **基金转换线第 5 步开工 · 第 0 批门禁 T-0 + T-0b 完成**:基线 510 → **516 passed / 3 skipped**。**T-0**:`tests/_ddl.py` 重写 `core_holding`(`qty`/`cost_amount`/`as_of`/`pnl_pct` + PK + `uk_cust_product`)+ 新增 `core_product_nav` + `_assert_ddl_aligned()` 建库自校验;2 处测试 INSERT 改 `qty` **并补 3 个 NOT NULL 列**(**原计划漏项**);`test_db.py` +3 用例(含**门禁反向验证的自动化用例**)。**T-0b(D20 账号分离)**:新增 `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) | AIcoding 第 5 步(T-0/T-0b 为阻断前置);用户「你进行下一步」 | tests/_ddl.py · tests/test_db.py · tests/conftest.py · tests/test_chat_tools.py · tests/test_concentration_c4.py · app/utils/db.py · app/config/settings.py · 4 个 Repository · **scripts/core/00-grant.sql(新)** · 开发计划-基金转换(§3.3) · 架构设计-基金转换(§11.1 实施记录) · 交接文档-基金转换(v1.2) · MEMORY / TODO / 当日日志 |
|
||||
|
||||
+14
-3
@@ -11,6 +11,8 @@
|
||||
|
||||
**当前进度:** 需求与表设计已定 · **风控模块 B1~B9b 全部完成(M2 tag risk-m2),M4 复核已闭环(2026-09-07),风控阶段 B 正式完结** · **Wave 0 已完成(2026-09-07,经独立 AI 评审闭环)**:T-01 JWT 鉴权(auth_service Auth SDK + deps 工厂替换 + X-Agent-Type 准入矩阵)/ T-02 审计中间件(http_access + 独立 request_id + 4xx/500 统一错误体 + input_guard_log 双写)/ T-06 chat 最小闭环(POST /api/chat + 会话落库 + Redis 窗口)/ T-07 LangGraph StateGraph 骨架 + DeepSeek(无 key 降级)· **T-04 Core RO Tool 节点已完成(2026-09-07)**:app/tool/core_tools.py 三只读 Tool + tool_service(意图/归属校验/run_tool)+ 图 tool 节点 + agent_tool_call 落库 + `utils/authz.py` 公共鉴权留痕,**321 测试绿(首评+复审双闭环)**。风控阶段 C 已完成(2026-09-07,345 绿,tag `risk-m3`,A-6 对话线验收通过,独立 AI 评审 PASS P0=0)。T-03 输入防护已完成(2026-09-07,378 绿,独立 AI 评审 PASS with findings P0=0):app/service/input_guard.py 注入词表 45 条纯函数检测 + oversize 4000 + actor 级 Redis 固定窗口限流 30 次/分(fail-open);chat 链路顺序 = 鉴权→准入→空白→限流 429→注入/超长 400→归属→会话,被拒 fail-fast 不建会话,blocked 落 input_guard_log(ENUM 四值已用满)。**阶段一「对齐 main 基准」AL-01~AL-08 已完成(2026-09-07,逐项独立 commit bb244f4~b5fd52e):适当性判定换核为 main 的 core_ro.check_suitability(C×R 矩阵表数据驱动,match_result 五值/JR-AST-012/FM-01/FM-03/JR-AST-PRO 契约,SUIT-001~008 退役);risk_suitability_log 重建 21 列;Core 表加 is_hnw/风评七新列(expires_at);种子 33 客户/14 产品;全量 406 passed 0 failed 0 skipped + uvicorn 冒烟三端点通过;risk-m1 已补打(指向 3c07de6)**。**下一步:阶段一验收门(**合并 main 前的最终交付检查点,非分支开发阻塞**;用户浏览器目视确认 UI)→ AL-09 合并 main → AL-10 PRD v1.2 → AL-11 docx 登记 → 阶段二 C4~C6(**已完成并打 `risk-m4` tag**)/ 前端 React 多 Agent 入口(HashRouter `web/` init)。****开发在分支 `risk-control-agent`(与 origin/main 已分叉:领先 84 提交 / 落后 0,origin/main 为分支祖先;**分支已推送远程,origin/risk-control-agent 同步于 `1d00e53`,本地跟踪已建立**;**架构改进与稳定性加固 T-101~T-202 已于 2026-09-09 完成并推送 origin/risk-control-agent(2d0e2fa..f733fc2 快进,pytest 510 绿,较 503 基线 +7 例:T-201.3 双层锁 6 例 + T-202 trace 顺序守卫 1 例),含 Redis 双层分布式锁 T-201 与审计中间件 trace 顺序守卫 T-202,均为文档/告警/锁原语修正、接口契约与表结构零变更;远程已提示走 PR 合并 main;**合并(risk-control-agent → main)由合并执行人负责,不归用户管**——AI 只负责把分支推到远程 + 更新文档,不得主动发起 PR / 执行合并,移交执行人按《合并注意事项-风控模块并入main.md》操作。** 旧文档中的 `feature/risk` 为过时口径)。**
|
||||
|
||||
**⚡ 并行新线 · 基金转换(convert)交易(2026-09-10 设计+开发计划闭环,**第 5 步进行中:T-0 / T-0b / T-1 已完成**):** `PRD-风控监测Agent` FR-1 一期显式拒收 convert,本线将其放开。**AIcoding 第 1~4 步已完成**:PRD **v0.9.1 定稿**(33 条外审闭环 + 2 处架构回填 + **费率分类修正**)→ 架构 **v1.0 定稿**(独立评审通过,13 条建议 **0 悬空**,接受 10 / 修正性接受 3 / 驳回 0)→ 门控 **M-7 已满足** → **开发计划 v1.0 已产出(`docs/项目框架设计/开发计划-基金转换交易.md`,**1,047 行**)并经**四轮**独立子代理审核收敛(**12 条意见 → 接受 11 / 驳回 1(附实测证据)/ 0 悬空**)。**第 5 步进行中:T-0 + T-0b + T-1 + T-2 + T-2b 已于 2026-09-10 完成(609 passed / 3 skipped;T-1 断言 8/8 PASS;T-2 纯函数包 7 文件 / 93 用例;T-2b 实算脚本 15/15 一致)**,**下一步 = T-3(并行组 A:T-3 / T-4 / T-5 可同时开工)**。**开工前置两个阻断项(✅ 2026-09-10 均已完成)**:**T-0**(sqlite/MySQL 结构对齐:`core_holding` 列名+PK **+ 补 `core_product_nav`**,建库自校验,**同步改 2 处测试 INSERT 并补 3 个 NOT NULL 列**)与 **T-0b**(**DB 账号分离 D20**:`xh_core_ro` SELECT 全库 / `xh_core_rw` **4 表写**无 DELETE·DDL / `xh_agent_rw` **`audit_log` 只授 SELECT+INSERT**(不可改删);**`conftest.py` 四处 engine 已显式 `role="admin"`**)。**遗留环境操作**:`scripts/core/00-grant.sql` 需管理员执行一次 + 写 `.env`,未执行时 3 条权限断言自动 skip,不阻塞 T-1。设计资产五件套:`docs/PRD/PRD-基金转换交易.md` · `docs/项目框架设计/架构设计-基金转换交易.md` · **`docs/项目框架设计/开发计划-基金转换交易.md`(新)** · `评审待办-风控主架构与基金转换.md` · `基金转换-审查意见处置表.md`。**开工前必读开发计划 §1.4(15 条代码事实)/ §1.5(7 条实现级裁定 R-a~R-g)/ §12(16 条回归面)** —— 尤其是 **R-a(弃用方言 UPSERT)· R-b(流水写 redeem/subscribe 不写 convert)· R-e(conftest 用 admin)** 三条,不读必踩。**交接入口:`docs/交接文档-基金转换.md`**。合规基准 = 证监会公告〔2025〕22 号。
|
||||
|
||||
**仓库地图:**
|
||||
|
||||
| 路径 | 状态 | 说明 |
|
||||
@@ -34,7 +36,7 @@
|
||||
| `scripts/core/*.sql` + `reset.ps1` | **已实现** | Core 模拟库 DDL + 种子 |
|
||||
| `scripts/agent/` `scripts/demo/` `scripts/dev/` | **已实现** | AML 名单种子 + 风控演示数据 + subscribe_alerts/rebuild_alerts + issue_dev_token(JWT 签发) |
|
||||
| `scripts/sync/*.py` | **已实现** | 归属同步 + Neo4j 全图 |
|
||||
| `tests/` | **已实现** | 36 个测试模块 510 用例(sqlite 隔离;DDL 单一事实源 `_ddl.py`;`test_integration_risk.py` 走真 MySQL + TRD-TEST- 前缀隔离;pytest 依赖装在系统 Python 3.13.14;阶段一基线 425→406,前端接入 B/C 后 **482→503**,架构改进 T-201.3+T-202 后 **503→510**)。**改路由必同步 `tests/test_main.py::test_all_routers_mounted` 的路径清单,否则必红** |
|
||||
| `tests/` | **已实现** | 36 个测试模块 516 用例(sqlite 隔离;DDL 单一事实源 `_ddl.py`;`test_integration_risk.py` 走真 MySQL + TRD-TEST- 前缀隔离;pytest 依赖装在系统 Python 3.13.14;阶段一基线 425→406,前端接入 B/C 后 **482→503**,架构改进 T-201.3+T-202 后 **503→510**)。**改路由必同步 `tests/test_main.py::test_all_routers_mounted` 的路径清单,否则必红** |
|
||||
| `docs/需求拆解/` | 已定 | 场景 P0、矩阵、合规原文 |
|
||||
| `docs/PRD/PRD-风控监测Agent.md` | **已冻结(v1.1)** | 风控 PRD v1.0 + v1.1 追加 FR-8/9/10(§4A)+ 规则表附录 |
|
||||
| `docs/项目框架设计/实现方案-风控追加需求v1.1-C4C6.md` | **已定稿** | C4~C6 编码依据(经独立 AI 评审修订闭环);分支/进度速览另见项目根 `交接文档.md` |
|
||||
@@ -42,6 +44,10 @@
|
||||
| `docs/项目框架设计/表设计/` | 已定 | Agent 共用 11 表 + agent 专用 SQL |
|
||||
| `docs/项目框架设计/Core模拟底座/` | 已定 | 无真实 Core 时的 L0 方案 |
|
||||
| `web/` | **不存在** | 前端 React 待 init |
|
||||
| `docs/PRD/PRD-基金转换交易.md` | **已定稿(v0.9.1)** | 基金转换线需求权威(FR-C1~C16 / §4 表结构 / §5 接口 / §9 验收);⚠️ **代码未实现** |
|
||||
| `docs/项目框架设计/架构设计-基金转换交易.md` | **已定稿(v1.0)** | 基金转换实现依据(D1~D20 / §5 事务 / §11.1 DB 账号 / §15 任务 T-0~T-13);⚠️ **代码未实现** |
|
||||
| `docs/项目框架设计/开发计划-基金转换交易.md` | **已定稿(v1.0 · 2026-09-10)** | **基金转换实现计划**(T-0~T-13 逐任务改法 + DoD + §1.4 **十五条代码事实核对表** + §1.5 **七条实现级裁定 R-a~R-g** + §12 **十六条回归面** + 四轮审核记录 + **§3.3 第 0 批 + §4.1 第 1 批执行记录**);**T-0/T-0b/T-1 已实现**,T-2 起待实现 · **开工必读** |
|
||||
| `docs/交接文档-基金转换.md` | **新增(2026-09-10)** | **基金转换线交接入口**——给下一会话 AI,读完即可开工 |
|
||||
|
||||
**本地 bootstrap(首次):** 完整步骤与前置说明见 `FLOW.md` §0(权威),速览:
|
||||
|
||||
@@ -64,6 +70,8 @@
|
||||
|
||||
**下一步开发(见 TODO):** **模块侧交付完毕(2026-09-07:全量 pytest 482 绿 + 接口实调验收通过——suitability/check 阻断+放行、simulate/trade 阻断、三条鉴权边界 401/403/403 契约零偏差;`risk-m1~m4` tag 齐)。合并 main 已移交合并执行人,操作手册《docs/项目框架设计/合并注意事项-风控模块并入main.md》(含基底锁定/20 冲突裁决/14 静默文件/三硬伤/合并后必测,实测数据编制)。模块侧开放项:chat 链路 risk_suitability_log.actor_id 落 SYSTEM 待评估 / 前端 React 多 Agent 入口(`web/` 未 init,归属待拍板)。**演示走查按 `docs/项目框架设计/演示SOP-风控模块.md`(debug 头通道仍有效;JWT 通道签发用 `scripts/dev/issue_dev_token.py`;演示库已按 AL-08 expires_at 新口径重灌)。知识库入库:`python scripts/kb/build_kb.py`(先启 Ollama;**Milvus 数据路径必须纯英文**——faiss 不支持中文路径,本机 .env 已配 C:/Users/YUAN/.jinrong/milvus/)。
|
||||
|
||||
**另(2026-09-10 待办)**:① **基金转换线**第 5 步进行中(**T-0 / T-0b / T-1 / T-2 / T-2b 已完成,下一步 = T-3**;设计 + 开发计划均已闭环,入口 `docs/交接文档-基金转换.md`);② ~~架构改进线收尾~~ —— **2026-09-10 已闭环结项**:§7.2 七项手工冒烟补跑 **7/7 PASS**、冒烟残留按 SOP §2 重灌双库清除、全量 **510 passed** 复绿;**「`037ce7e` 未 push」的旧表述已作废**(实测 `git ls-remote`:远程 `risk-control-agent` = `fffb78a` = 本地 HEAD,`037ce7e` 在其祖先链上,早已推送;本地 `git branch -vv` 显示 `origin/risk-control-agent: gone` 只是远程跟踪引用失效,`git fetch` 即恢复,非远程分支被删)。入口 `docs/交接文档-架构改进.md`。
|
||||
|
||||
**禁止(改代码前必记):** Core 正式 C1~C5 不可被画像覆盖 · 审计表只 INSERT · 代理人草稿不外发 · 仅 R-02 可阻断交易 · 四 Agent 不互调 LLM。
|
||||
|
||||
**里程碑 tag 中文对照(tag 本体保留英文原名——git 技术标识符需跨平台/脚本稳定引用,符合「技术字面量保留英文」约定;中文名经对照表统一):**
|
||||
@@ -145,7 +153,7 @@ Core 模拟:scripts/core/reset.ps1 · 文档 docs/项目框架设计/Core模
|
||||
种子:scripts/agent/seed-aml-list.sql(AML 名单)· scripts/demo/prepare_risk_demo.sql(reset 后重跑)
|
||||
依赖:requirements.txt(LangGraph + langchain-core/openai + FastAPI + SQLAlchemy)
|
||||
启动:uvicorn app.main:app --reload → GET /health
|
||||
测试:python -m pytest(510 用例;集成测试需本机演示数据,未灌库时自动 skip)
|
||||
测试:python -m pytest(516 用例;集成测试需本机演示数据,未灌库时自动 skip)
|
||||
运维/演示脚本:scripts/demo/subscribe_alerts.py(订阅推送演示)· rebuild_alerts.py TRD-xxx(引擎异常补偿重放)
|
||||
JWT 联调:python scripts/dev/issue_dev_token.py --sub STAFF-30001 --roles risk_officer(+ Authorization: Bearer + X-Agent-Type)
|
||||
配置:.env(见 .env.example)
|
||||
@@ -172,6 +180,8 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md
|
||||
| `docs/需求拆解/` | 业务原文(场景、矩阵、合规) |
|
||||
| `docs/项目框架设计/` | 表结构、JWT 手册、Core 模拟、技术版本 |
|
||||
| `docs/业务记忆管理/` | Redis 短期 vs MySQL/Milvus/Neo4j 权威记忆 |
|
||||
| `docs/交接文档-基金转换.md` | **基金转换线开工入口** —— 接手本线先读这一份(不必重读代码) |
|
||||
| `docs/PRD/PRD-基金转换交易.md` · `docs/项目框架设计/架构设计-基金转换交易.md` | 基金转换需求与实现依据(**v0.9.1 / v1.0,代码未实现**) |
|
||||
|
||||
缺 `docs/memory/*` 文件:按 project-memory-kit 同名补回,**禁止空模板盖进度**。
|
||||
|
||||
@@ -189,6 +199,7 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md
|
||||
2. 改动属于 api / service / tool / repository 哪一层?
|
||||
3. 是否需 customer_id 归属与 JWT RBAC?
|
||||
4. Core 是模拟库只读还是 agent 库读写?
|
||||
5. 如何验证?(`python -m pytest` 全量(当前 **510 绿**)· uvicorn 启动 + /health · SQL / sync 脚本 · 对照 REQUIREMENTS 验收列)
|
||||
5. 如何验证?(`python -m pytest` 全量(当前 **516 passed / 3 skipped**,基线 510)· uvicorn 启动 + /health · SQL / sync 脚本 · 对照 REQUIREMENTS 验收列)
|
||||
6. **当前有哪两条并行线?**(① 风控/架构改进线:**已结项**(510 基线绿、§7.2 七项冒烟 7/7 PASS、`037ce7e` 已核实早已推送);② **基金转换线**:设计闭环,**第 5 步进行中 —— T-0 / T-0b / T-1 / T-2 / T-2b 已完成(609 passed),下一步 = T-3**)——动代码前先确认自己属于哪条线,别混淆前置条件。
|
||||
|
||||
大任务:FRAMEWORK/FLOW 与实现状态不符时先更新 memory 再编码(用户确认跳过除外)。
|
||||
|
||||
@@ -7,6 +7,22 @@
|
||||
|
||||
**阶段一 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)**(2026-09-10):**设计 + 开发计划均已闭环** —— PRD **v0.9.1** + 架构 **v1.0** + 独立评审 13 条 **0 悬空**(接受 10 / 修正性接受 3 / 驳回 0),门控 **M-7 已满足**;**第 4 步开发计划 v1.0 已产出并经独立审核**(4 条意见全接受、**驳回 0**,含新增 2 条回归面 R15/R16 + R-c 双条修订);**第 5 步进行中**:**T-0 + T-0b + T-1 + T-2 + T-2b 均于 2026-09-10 完成**(**609 passed / 3 skipped**;T-1 断言 **8/8 PASS**、T-2 纯函数 **93 用例**、T-2b 实算 **15/15 一致**),**下一步 = T-3(并行组 A:T-3 / T-4 / T-5 可同时开工)**。两个阻断前置(**T-0** sqlite/MySQL 列名统一 + 建库自校验 · **T-0b** DB 账号分离 D20:`xh_core_ro`/`xh_core_rw`/`xh_agent_rw`)**均已落地**。**入口:`docs/交接文档-基金转换.md`**(读这一份即可开工);**开工前必读开发计划 §1.4(15 条代码事实)+ §1.5(7 条实现级裁定 R-a~R-g)+ §12(16 条回归面)**。
|
||||
|
||||
### 基金转换线待办(推荐顺序)
|
||||
|
||||
- [x] **【第 4 步】产出开发计划** —— **完成(2026-09-10)**:`docs/项目框架设计/开发计划-基金转换交易.md`(**1,047 行**,含 T-0~T-13 任务拆解 + 逐条改法 + DoD + **四轮审核记录**)。**四轮独立子代理审核已收敛:12 条意见 → 接受 11 / 驳回 1(附实测证据)/ 0 悬空**(第一轮:补 R15 真 MySQL 集成用例 `test_integration_risk.py:426-437` + 补 R16 redeem 用例 + **R-c 拆「降级规则 + 覆盖率补偿」双条** + F-11 计数 11→13 更正;第二轮:发现**执行权锁是请求粒度**致并发首次建行撞 UNIQUE → **R-a 增第 ③ 步 `IntegrityError` 回退 UPDATE**,并实测排除 `WHERE NOT EXISTS`/`FROM DUAL` 两个不可移植写法;第三轮:修 §14「四道→五道」口径不一致 + 驳回「SQLAlchemy 会抛 PendingRollbackError」的推测;第四轮:终验通过、无「声明已改实际未改」残留)。**本计划新增的增量资产**:§1.3 两处 PRD-架构口径裁定(W-1/W-2)· §1.4 十五条代码事实核对表 · §1.5 七条实现级裁定(**R-a「条件 UPDATE → 首次 INSERT → `IntegrityError` 回退 UPDATE」三步**、R-b 流水写 redeem/subscribe 不写 convert、R-c 批次降级+覆盖、R-d `sum_trades_on_date` 去重、R-e conftest 用 admin 角色、R-f `core_product_nav` 并入 T-0、R-g 自校验落 `_ddl.py`)· §12 十六条回归面 · §13 PRD 19 条验收映射 · §14 新增实现级风险表
|
||||
- [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)
|
||||
- [ ] **【T-3 起】** 仓储与锁(并行组 A:T-3 / T-4 / T-5 可同时开工)→ T-6 / T-7 → T-8 / T-9 / T-11 → **T-10 高风险单列** → T-12 → **T-13「50 并发压测 + 性能补录」**(最后跑,产出 PRD §9 第 18 条实测值)。**T-13 内部顺序**:先 50 并发压测 → 再性能实测补录 → 最后 PRD §5.3 数字回填(详见架构 §15 + 开发计划 §2~§10)
|
||||
|
||||
> **第 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)。
|
||||
> **下一步 = T-3**(`core_ro` 五个新方法:`get_nav_as_of` / `get_redeem_fee_rules` / `list_share_lots` / `sum_remain_qty` / `get_holding` + 新增 `app/repository/share_lot_repository.py`;DoD 见开发计划 §5.1)。**并行组 A 的 T-3 / T-4 / T-5 可同时开工**。
|
||||
|
||||
> ⚠️ **基金转换的 T-0b 与下方「架构改进第 3/4 批」的 `core_ro` 只读账号是同一件事** —— 已由本线定案为 D20,**不再挂在架构改进线**(该线原「不要做」清单已更新)。
|
||||
|
||||
## 待办(推荐顺序)
|
||||
|
||||
- [x] **【阶段一验收门】接口实调验收** —— **通过(2026-09-07 委托 AI 代验:三接口契约 + 三鉴权边界全部符合;前端 web/ 未 init,验收口径为后端接口验证)**
|
||||
@@ -35,6 +51,7 @@
|
||||
|
||||
## 待办(模块侧开放项)
|
||||
|
||||
- [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**。入口:`docs/交接文档-架构改进.md`
|
||||
- [ ] 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 绿**。遗留:无心跳帧、断连留空会话待清理
|
||||
|
||||
Reference in New Issue
Block a user