Neo F1-5 反馈: "让沙箱起到其真正的作用. 真正的模拟日期, 仅能看到沙箱设定日期 及之前日期的数据, 并运行 AI 的各个业务." 调研发现 (4 个并行子代理): batch-run 端点 _run_batch 是空壳 stub (只 logger.info, 实际不跑 AI), GUC apply_runtime_session_vars 0 处调用 (dead code), 7 张业务表 6 张有 runtime 复合索引唯独 ai_run_logs 漏建, App2/2a 3 行 _calc_date_range 漏传 ref_date. 本 commit (F1-5a 阶段 A 主体, F1-5b 后续完整 zqyy_app RLS 视图层): 后端核心: - admin_service.py: _run_batch 真实化 (Semaphore(5)+asyncio.gather+ return_exceptions=True+ctx_snapshot 防漂移); estimate 入口抓 RuntimeContext 快照, confirm 取出传给 worker - admin_ai.py: confirm_batch_run lazy 注入 dispatcher - admin_service.retry_trigger_job: INSERT 落 runtime_mode + sandbox_instance_id 列 (用 runtime_insert_columns helper) - runtime_context.py: get_runtime_context 加 bind_to_session 参数, 激活 GUC app.current_business_date / app.current_runtime_mode - run_log_service.create_log: 启用 bind_to_session=True 试点 App2/2a 3 行 ref_date 修复: - app2_finance_prompt.py:817 储值卡余额变化板块 - app2_finance_prompt.py:841 日粒度 series + 异常检测窗口 - app2a_finance_area_prompt.py:466 区域日粒度 series DB: - migrations/20260505__ai_run_logs_runtime_index.sql: 补 (site_id, runtime_mode, sandbox_instance_id, created_at DESC) 复合索引 前端: - AIOperations.tsx: 顶部加 sandbox 模式提示条 (Alert 显示 sandbox_date + sandbox_instance_id + 影响范围 + 切回 live 入口) 未做 (留 F1-5b 完整 zqyy_app RLS 视图层一并): - B1 admin_service 6 处 CURRENT_DATE -> business_date - B2 fdw_queries 异常分支兜底 - GUC 完整传递 (fdw_queries / page_context 等) - 测试 3 套 (.gitignore:71 排除, F2-2 入仓时 commit) - P20 SPEC \xa76/\xa710/\xa711/\xa715 (F1-5b 完整收口后同步更准确) Neo 决策: docs/_overview/wave1-findings/F1-5-impl-decisions.md 详见 docs/audit/changes/2026-05-05__wave1_f1_5a_sandbox_batch_run.md
429 lines
20 KiB
Markdown
429 lines
20 KiB
Markdown
# F1-5 沙箱 batch-run 接入 runtime_context — 实施决策卡
|
||
|
||
> 日期:2026-05-05
|
||
> 触发:F1-5 前置调研完成,4 份子代理报告整合后剩 6 项实施决策待 Neo 拍板
|
||
> 调研依据:`runtime_context.py:264` / `admin_service.py:622-631` / W1-T7 PRD 批 1(`batch1-runtime-context-and-ai.md:494-547`)/ P20 SPEC §10/§14
|
||
> 用法:Neo 在每个决策卡下面用 *斜体* 写反馈/选择,Claude 据此实施
|
||
|
||
---
|
||
|
||
## 决策总览
|
||
|
||
| # | 决策项 | Claude 推荐 | Neo 决定 |
|
||
|---|---|---|---|
|
||
| D1 | batch executor 实现方式(Semaphore / 全并发 / 串行 / TaskQueue) | Semaphore(3) + asyncio.gather + return_exceptions | (待填) |
|
||
| D2 | GUC C 方案处置(全做 / 移除 / 混合) | 混合(纳入阶段 A) | (待填) |
|
||
| D3 | 测试覆盖是否纳入 F1-5(同 commit) | 纳入(阶段 A) | (待填) |
|
||
| D4 | 走查 mock 数据(切 sandbox=2026-03-01) | 授权我用 PATCH 端点切 + 完成后切回 live | (待填) |
|
||
| D5 | commit 拆分(1 个 / 2 个 / 3 个) | 1 个统一 commit | (待填) |
|
||
| D6 | P20 SPEC §11/§15/§10 文档同步更新 | 纳入 F1-5 commit | (待填) |
|
||
|
||
---
|
||
|
||
## D1. batch executor 实现方式
|
||
|
||
### 关联页面/接口
|
||
|
||
- 后端:`apps/backend/app/services/ai/admin_service.py:622-631`(`_run_batch` 当前是空壳 stub)
|
||
- 后端:`apps/backend/app/services/ai/admin_service.py:576-620`(`estimate_batch` / `confirm_batch`)
|
||
- 后端:`apps/backend/app/routers/admin_ai.py:269-293`(`POST /batch-run` / `POST /batch-run/confirm`)
|
||
- 前端:`apps/admin-web/src/api/adminAI.ts:285,290`(`createBatchRun` / `confirmBatchRun`)
|
||
- 前端:`apps/admin-web/src/pages/AIOperations.tsx`(批量执行 Modal,两步操作)
|
||
|
||
### 业务背景
|
||
|
||
`/admin/ai/batch-run`(两阶段提交)是 admin-web 的批量 AI 调用入口。运维选 N 个 app_type × M 个 member,**估算 token / 调用次数 → 看到预估同意 → confirm 后台异步真跑**。两阶段防误操作打爆 token 预算(因为 1000 个会员一次跑可能几百元)。
|
||
|
||
**当前代码事实**:
|
||
- `estimate` 端点工作 ✓ 算 token / calls
|
||
- `confirm` 端点工作 ✓ 返 `{status: started}`,创建 `asyncio.create_task(self._run_batch(...))`
|
||
- **`_run_batch` 是占位假动作**:只 `logger.info`,**实际不跑 AI 调用**(注释"实际执行逻辑在路由层通过 dispatcher.handle_trigger 驱动"是 stub 假话)
|
||
- 路由层并未注入 dispatcher 到 service 层
|
||
|
||
### 冲突逻辑
|
||
|
||
当前 batch-run 端点 = **假动作**:
|
||
- admin 看到 estimate 估算 / confirm 返 success
|
||
- 后台 logger 只打一行 "_run_batch invoked",**0 个 AI 实际调用**
|
||
- ai_run_logs / ai_cache 没有 batch 相关记录
|
||
- 这违反 PRD §"业务语义":"投递到 dispatcher"——但实际没投递
|
||
|
||
### 业务联系
|
||
|
||
- F1-5(本任务):batch-run 接入 sandbox runtime_context — 但前提是 batch-run **真的能跑**,所以**必须先把 executor 补完**
|
||
- W1-T7 PRD 批 1 评估问题已标 P0:"batch_id 生命周期未声明 / 状态查询缺失 / member_ids 无上限"——但**没标"实际不执行"**(文档评估只看 schema 没看深度代码)
|
||
- Wave 2 计划:加 `GET /batch-run/{batch_id}/status` 进度查询(本次不做)
|
||
- AIPrewarm.tsx(/ai/prewarm)分组展示 72 组合,目前仅"每行触发"用单点 `run/{app_type}` 路径,batch-run 路径补完后可统一
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 影响范围 | 复杂度 |
|
||
|---|---|---|
|
||
| **a 全并发**(asyncio.gather 无 sem) | 1000 个 member 一次性发 → DashScope 限流熔断 / token 预算瞬间打爆 | 极不安全 ❌ |
|
||
| **b 串行**(顺序 await) | 1000 调用 × 2-5 秒 = 30+ 分钟,admin 看不到任何进度,confirm 时刻没回应 | 慢但安全 |
|
||
| **c Semaphore 限并发**(我推荐 N=3) | 3 个并发,1000 调用约 10 分钟,与现有 dispatcher 熔断/限流配合 | 平衡 ✅ |
|
||
| **d TaskQueue 推送**(走 worker 消费) | 引入跨进程依赖,与 confirm 端点的 `asyncio.create_task` 模型不一致 | 改造大 |
|
||
|
||
**c 方案细节**:
|
||
```python
|
||
async def _run_batch(self, params: dict, ctx_snapshot: RuntimeContext) -> None:
|
||
sem = asyncio.Semaphore(3)
|
||
tasks = []
|
||
for app_type in params["app_types"]:
|
||
for member_id in params["member_ids"]:
|
||
async def _one(at=app_type, mid=member_id):
|
||
async with sem:
|
||
await self._dispatcher.run_single_app(
|
||
app_type=at,
|
||
context={
|
||
"site_id": params["site_id"],
|
||
"member_id": mid,
|
||
"business_date": ctx_snapshot.business_date.isoformat(),
|
||
},
|
||
triggered_by=f"batch:{params['batch_id']}",
|
||
)
|
||
tasks.append(asyncio.create_task(_one()))
|
||
await asyncio.gather(*tasks, return_exceptions=True) # 单失败不连坐
|
||
```
|
||
|
||
- `Semaphore(3)`:限并发 3,与 dispatcher 现有 circuit_breaker / rate_limiter 配合,不打爆 DashScope 1000 RPM 限制
|
||
- `triggered_by=f"batch:{batch_id}"`:打标 ai_run_logs,Wave 2 加进度查询 `WHERE triggered_by LIKE 'batch:<id>%'` 即可统计
|
||
- `ctx_snapshot`:estimate 阶段抓 RuntimeContext 快照,worker 用快照值,**避免** Neo 在 estimate→confirm 间切 sandbox 模式造成污染
|
||
- `return_exceptions=True`:1 个 member 失败不阻断其他 member,失败信息已写 ai_run_logs
|
||
|
||
### 推荐选项
|
||
|
||
**c 方案 — Semaphore(3) + asyncio.gather + return_exceptions=True + ctx_snapshot**
|
||
|
||
### 建议与理由
|
||
|
||
- 与现有架构(asyncio.create_task / dispatcher 限流)一致,无新引入复杂度
|
||
- 限并发 3 是经验值(可调),既快(10 分钟)又安全(不爆限流)
|
||
- 快照设计避免最大数据漂移风险(Neo 切模式时 batch 已在跑)
|
||
- `triggered_by` 打标为 Wave 2 进度查询埋好钩子
|
||
- 单失败不连坐符合"批量任务"行业惯例
|
||
|
||
**Neo 反馈**:
|
||
|
||
*同意 c 方案 改并发数 N=5 改进 ctx_snapshot 合适的时机你来定)*
|
||
|
||
---
|
||
|
||
## D2. GUC C 方案处置
|
||
|
||
### 关联页面/接口
|
||
|
||
- 后端:`apps/backend/app/services/runtime_context.py:244-263`(`apply_runtime_session_vars` 函数,**当前 dead code**)
|
||
- DB(ETL):`db/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql`(26 个 `app.v_*` 视图加业务日上界)
|
||
- DB(ETL):`app.business_date_now()` 函数(读 GUC `app.current_business_date`,fallback 真实 today)
|
||
- DB(zqyy_app):**无 RLS 视图层**,业务库 SQL 直查 `biz.*` 表
|
||
- SPEC:`docs/prd/specs/P20-runtime-context-sandbox.md` §6(C 方案 GUC 路线)
|
||
|
||
### 业务背景
|
||
|
||
P20 沙箱机制要求:Neo 切 sandbox=2026-03-01 后,后端跑 AI / 查询数据时,**只能看到 2026-03-01 及之前数据**(防"未来数据泄露")。
|
||
|
||
实现方案有两条路线:
|
||
- **A 应用层方案**:每个查询函数都加 `business_date` 参数,SQL 里 `WHERE date <= %s` 显式裁剪
|
||
- **C GUC 数据库层方案**:DB 连接级 `SET LOCAL app.current_business_date='2026-03-01'`,所有视图自动按 `app.business_date_now()` 裁剪(代码不需要每个 SQL 都改)
|
||
|
||
### 冲突逻辑
|
||
|
||
**当前现状是"做了一半的 C 方案 + 大部分 A 方案":**
|
||
|
||
| 库 | 方案 | 现状 |
|
||
|---|---|---|
|
||
| etl_feiqiu(ETL) | C 方案 | ✅ 26 个视图加上界 |
|
||
| zqyy_app(业务) | A 方案 | ✅ 应用层多数已传(7 prompt + 4 fetcher + board_service + fdw_queries) |
|
||
| `apply_runtime_session_vars` | C 方案"开关" | ❌ **全仓库 0 处调用** |
|
||
|
||
**致命隐性 bug**:`apply_runtime_session_vars` 是激活 ETL 库 26 个视图的开关,但开关从未被打开 → ETL 库视图调 `app.business_date_now()` 拿不到 GUC → fallback 真实 today → **Neo 切 sandbox=2026-03-01 后,后端走 ETL 库 `app.v_*` 视图查询时,仍能看到 2026-05-05 的"未来"数据**。
|
||
|
||
### 业务联系
|
||
|
||
- F1-5 主诉求"仅能看到沙箱设定日期及之前数据" — 当前没满足(因为 GUC 开关没开)
|
||
- W1-T1 看板沙箱接入(2026-05-04)— 走 A 方案在前端 isCurrentMonthFilter 处理,与本决策正交
|
||
- W1-T2 SCD2 视图统一(2026-05-04)— 走 C 方案,本决策对其有影响(SCD2 视图也读 GUC)
|
||
- Wave 2 P1-1 schema 迁移、P1-6 触发器合并 — 与 C 方案 zqyy_app 视图层延伸相关
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 改动 | 工作量 | 业务效果 |
|
||
|---|---|---|---|
|
||
| **a 全做(扩 zqyy_app)** | dispatcher 入口调 + zqyy_app 加 RLS 视图层(C 方案落地) | 大(2-3 天,涉及 RLS 双 schema 规则) | 两库都靠 GUC 自动保护 |
|
||
| **b 移除** | 删 `apply_runtime_session_vars` + 文档移除 C 方案 + ETL 库 26 视图改回不带 GUC | 中(回滚 C 方案,审计成本) | 只靠应用层 A 方案 |
|
||
| **c 混合(我推荐)** | dispatcher 入口调一次 `apply_runtime_session_vars` + zqyy_app 库继续 A 方案,**不新增 RLS 视图层** | 极小(1-2 行代码 + 几个调用点) | ETL 库 C 方案激活(自动保护)+ zqyy_app 应用层 A 方案(显式保护)— 两库都安全 |
|
||
|
||
### 推荐选项
|
||
|
||
**c 混合方案 — 纳入阶段 A**(F1-5 内做)
|
||
|
||
### 建议与理由
|
||
|
||
- ETL 库 C 方案是 2026-05-02 的**已完成资产**,丢掉浪费(b 方案)
|
||
- zqyy_app 库 RLS 视图层从无到有是**新建工作量**,且应用层 A 方案已做 80%(只剩 admin_service 的 `CURRENT_DATE`,在阶段 B1 已纳入),没必要重复建第二套保护(a 方案过度)
|
||
- c 方案只需要 dispatcher / batch executor 拿 DB 连接后调一次 `apply_runtime_session_vars(conn, ctx=ctx_snapshot)` → ETL 库 26 个视图**立即激活**
|
||
- 业务结果:Neo 切 sandbox 后,ETL 库视图自动裁剪未来数据,业务库由应用层显式传保护,**两库都安全**
|
||
- 副作用:`docs/prd/specs/P20-runtime-context-sandbox.md` 需要把 §6 中"zqyy_app 加 RLS 视图层"的规划项移除或标"永不做"
|
||
|
||
**Neo 反馈**:
|
||
|
||
*能不能完整且统一?符合规范化的项目架构设计,保持一致,后续也能形成规范延续下去。*
|
||
|
||
---
|
||
|
||
## D3. 测试覆盖是否纳入 F1-5
|
||
|
||
### 关联页面/接口
|
||
|
||
- `apps/backend/tests/`(`tests/tests/unit/`、`tests/tests/integration/`)
|
||
- `apps/backend/tests/test_runtime_context*.py`(**当前不存在**)
|
||
- `apps/backend/tests/test_admin_ai_*.py`(已存在但 0 处 sandbox 覆盖)
|
||
|
||
### 业务背景
|
||
|
||
P20 SPEC §8 验收标准 AC1-13 含"沙箱模式数据隔离 / namespace 前缀正确 / 业务日上界生效"等多项硬性要求。当前**全仓库 0 测试覆盖** sandbox/runtime_context:
|
||
- `apply_runtime_session_vars`、`namespace_ai_target_id`、`task_runtime_filter`、`business_date_upper_bound_sql` 全无 unit
|
||
- `_run_batch` 即使补完 executor,无 integration 测试验证 ctx_snapshot 不污染
|
||
- dispatcher 落 runtime 列无回归保护
|
||
|
||
### 冲突逻辑
|
||
|
||
- F1-5 是 P0-7 沙箱主线**必修**(P20 验收前提)
|
||
- 但调研发现 0 测试 → 无回归保护 → 上线后任何后续改动都可能悄无声息破坏 sandbox 隔离
|
||
- 这是 W1 findings F1-1 反馈"长事务幂等"同类风险:有规则没测试 = 规则形同虚设
|
||
|
||
### 业务联系
|
||
|
||
- W1 findings F1-1 长事务幂等:Neo 反馈"我们之前因没测试导致 mock/prod 偏差"(类似教训)
|
||
- F2-2 tests/ 入仓(Wave 5)— 测试需要先入仓才能跑 CI,但本地测试不需要
|
||
- 测试规范:`docs/_overview/03-test-spec.md` L1-L5 五级测试
|
||
- `.gitignore:71` 排除 tests/ → 本地写测试不能 commit(F2-2 待解决)
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 改动 | 工作量 |
|
||
|---|---|---|
|
||
| **a 纳入 F1-5(我推荐)** | 写 3 套测试:test_runtime_context.py(unit 13 API)/ test_admin_ai_batch_runtime.py(integration estimate→切模式→confirm 用快照)/ test_dispatcher_runtime.py(_run_step 落 runtime 列) | 1-2 小时(主要是 fixture 准备) |
|
||
| **b 留 Wave 2** | 现在不做,Wave 2 测试专项做 | 0(此次)+ Wave 2 半天 |
|
||
| **c 简化版纳入** | 只写 1 套 integration 测试(estimate→切模式→confirm 用快照),unit 留 Wave 2 | 30 分钟 |
|
||
|
||
### 推荐选项
|
||
|
||
**a 纳入 F1-5 阶段 A**
|
||
|
||
### 建议与理由
|
||
|
||
- 沙箱机制是 P0 主线,无测试上线极高风险
|
||
- 1-2 小时 vs 上线后回归 bug 抓虫 = 投入产出比极高
|
||
- F2-2 tests/ 入仓未做不阻塞:测试本地写本地跑,Wave 5 入仓时一并迁移
|
||
- W1 findings 教训告诉我们:不写测试的"机制"不是真机制
|
||
- 备选:如果时间紧,接受 c 方案(只 integration)
|
||
|
||
**Neo 反馈**:
|
||
|
||
*同意 a 纳入*
|
||
|
||
---
|
||
|
||
## D4. 走查 mock 数据(切 sandbox=2026-03-01)授权
|
||
|
||
### 关联页面/接口
|
||
|
||
- 后端:`apps/backend/app/routers/admin_runtime_context.py:95`(`PATCH /api/admin/runtime-context`)
|
||
- DB:`test_zqyy_app.biz.site_runtime_context`(测试库唯一 site `2790685415443269` 当前 `mode='live'`)
|
||
- W1-T8 §14 走查清单:`docs/prd/specs/P20-runtime-context-sandbox.md:487-566`
|
||
|
||
### 业务背景
|
||
|
||
W1-T8 §14 成果层走查需要在 test_zqyy_app **切 sandbox=2026-03-01**(与 W1-T1/T2 测试日期同步),实地验证:
|
||
- admin-web 顶部 sandbox 提示条显示
|
||
- 触发 1 次 app2_finance 后 `ai_run_logs.runtime_mode='sandbox'` + prompt JSON `current_time='2026-03-01 HH:MM'` + `ai_cache.target_id` 含 `sbx_*:` 前缀
|
||
- ETL 库 `app.v_*` 视图业务日上界生效(查不到 03-01 之后数据)
|
||
|
||
完成走查后**切回 live**。
|
||
|
||
### 冲突逻辑
|
||
|
||
- CLAUDE.md 测试与验证环境规范要求:"使用测试库,禁止连正式库" — 切 sandbox 是测试库操作,合规
|
||
- CLAUDE.md 不破坏原则:"不在测试库以外执行 DDL / TRUNCATE / DELETE" — 切 sandbox 是 UPDATE 不是 DDL,合规
|
||
- 但 mode 切换是状态改动,需要 Neo 显式授权
|
||
|
||
### 业务联系
|
||
|
||
- F1-5 走查必须先切 sandbox 才能验证(否则 live 模式下所有 sandbox 代码路径都不会触发)
|
||
- 切完 sandbox 不切回会污染后续测试(下次有人跑测试拿到的是"虚拟时间")
|
||
- audit_log 会记录切换动作(switch_runtime_context 内部已经写 audit)
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 改动 | 风险 |
|
||
|---|---|---|
|
||
| **a 授权 PATCH 端点切**(我推荐) | 调 `PATCH /api/admin/runtime-context` 切 sandbox=2026-03-01 + 走查完切回 live | 测试库改动,有 audit 记录 |
|
||
| **b 授权 SQL 直改** | `UPDATE biz.site_runtime_context ... WHERE site_id=2790685415443269` | 绕过 audit,不推荐 |
|
||
| **c 不授权,Neo 自切** | 等 Neo 手动切完 + 通知 Claude 跑走查 | 多一轮等待,但更稳 |
|
||
| **d 走查时再决定** | F1-5 改完之后再问授权 | 推迟决策,减少认知负担 |
|
||
|
||
### 推荐选项
|
||
|
||
**a 授权 PATCH 端点切 + 完成后切回 live**
|
||
|
||
### 建议与理由
|
||
|
||
- PATCH 端点是正式入口,自带 audit,合规
|
||
- 走查后立即切回 live 避免污染
|
||
- Claude 实施 F1-5 期间不需要切(F1-5 单元测试可用 mock RuntimeContext);只在 W1-T8 走查阶段切一次
|
||
- 切换是 idempotent 操作,失败可回滚
|
||
- 如果不放心,可以选 c(走查时 Neo 手动切)— 这是最保守的
|
||
|
||
**Neo 反馈**:
|
||
|
||
*授权 a*
|
||
|
||
---
|
||
|
||
## D5. commit 拆分
|
||
|
||
### 关联页面/接口
|
||
|
||
- 阶段 A(MVP):`admin_service.py` _run_batch + estimate snapshot / `admin_ai.py` 路由 / `app2_finance_prompt.py` 3 行 / `retry_trigger_job` SQL / `AIOperations.tsx` 提示条 / 新 migration `<日期>__ai_run_logs_runtime_index.sql`
|
||
- 阶段 B(漂移防御):`admin_service.py` 6 处 SQL CURRENT_DATE / `fdw_queries.py:113,2552` 兜底
|
||
- 测试:3 个新 test 文件(若 D3 选 a)
|
||
- SPEC 同步:`P20-runtime-context-sandbox.md` §10/§11/§15(若 D6 选纳入)
|
||
|
||
### 业务背景
|
||
|
||
F1-5 涉及 ~10 个文件改动,跨后端 / 前端 / DB / 测试 / 文档 5 类。如何拆 commit 影响:
|
||
- 回滚粒度(出问题能精准 revert)
|
||
- 主题清晰度(commit log 可读)
|
||
- review 难度(reviewer 一次看多少)
|
||
|
||
### 冲突逻辑
|
||
|
||
| 维度 | 1 个 commit | 2 个 commit | 3 个 commit |
|
||
|---|---|---|---|
|
||
| 主题 | F1-5 完整收口 | A 沙箱接入 / B 漂移防御 | DB / Backend / Frontend |
|
||
| 回滚 | 全或无 | 中粒度 | 细粒度 |
|
||
| 可读性 | 包大 | 中 | 多 |
|
||
|
||
CLAUDE.md 提倡"逻辑独立的改动各自 commit",但本次所有改动都属于 F1-5 单一主题。
|
||
|
||
### 业务联系
|
||
|
||
- F3-2C 之前拆 2 commit(A 处置 + B 防御 hook)— 因为是 2 个独立主题
|
||
- F1-5 是单一主题:让沙箱 batch-run 真正生效
|
||
- Wave 1 Day 1-4 多数是 1 个 commit / 任务
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 适用 |
|
||
|---|---|
|
||
| **A 1 个 commit(我推荐)** | 单一主题,回滚一气呵成,改动文件 ~10 个可控 |
|
||
| B 2 个 commit | A:核心接入 / B:漂移防御 — 主题略有差异 |
|
||
| C 3 个 commit | DB/Backend/Frontend — 跨层拆分,但每个 commit 不能独立运行(后端依赖 DB 索引,前端依赖后端字段) |
|
||
|
||
### 推荐选项
|
||
|
||
**A 1 个 commit:`fix(ai): F1-5 沙箱 batch-run 接入 runtime_context(含 P20 漂移防御 + GUC C 方案激活)`**
|
||
|
||
### 建议与理由
|
||
|
||
- F1-5 是单一主题,拆 commit 反而割裂上下文
|
||
- 改动文件 10 个可读,commit message 清楚说明
|
||
- 跨层依赖(DB 索引 → 后端代码 → 前端组件)不能各自独立运行,拆 commit 后中间状态不可用
|
||
- 历史经验:F3-2C 拆 2 commit 是因为有 2 个独立主题(内容拆分 vs 防御机制),F1-5 不一样
|
||
|
||
**Neo 反馈**:
|
||
|
||
*同意 1 个 commit*
|
||
|
||
---
|
||
|
||
## D6. P20 SPEC §11/§15/§10 文档同步更新
|
||
|
||
### 关联页面/接口
|
||
|
||
- `docs/prd/specs/P20-runtime-context-sandbox.md`
|
||
- §6 GUC C 方案路线(若 D2 选 c 混合,需要标"zqyy_app RLS 视图层不做")
|
||
- §10 跨模块覆盖矩阵(app2/app2a 标记从 "?" 改 ✅)
|
||
- §11 已知遗漏(删除 batch-run 相关项)
|
||
- §15 变更记录(追加 2026-05-05 F1-5)
|
||
- 可能 §14 走查归档章节(走查后追加结果链接)
|
||
|
||
### 业务背景
|
||
|
||
P20 SPEC 是沙箱机制的权威设计文档。F1-5 是 P20 落地的"最后一公里",完成后 SPEC 必须同步:
|
||
- §10 矩阵反映现状(否则未来 reviewer 看 SPEC 仍会以为有缺失)
|
||
- §11 已知遗漏要把 batch-run 移除(已修复)
|
||
- §15 变更记录追加里程碑
|
||
|
||
不同步会导致 SPEC 和实际代码漂移,违反 W1 findings F2-1 教训(OpenAPI 28 天 stale 同类问题)。
|
||
|
||
### 冲突逻辑
|
||
|
||
- CLAUDE.md 数据库 Schema 变更规则:"必须同步 docs/database/" — 类似规则适用 SPEC
|
||
- W1 findings F2-1 教训:文档与代码漂移会让审计失效
|
||
|
||
### 业务联系
|
||
|
||
- F2-1B 防御机制 hook(已生效):router 改动会提醒抓 OpenAPI — 类似机制对 SPEC 缺失
|
||
- W1 findings F1-1 长事务幂等:文档没说 → 没人能验收
|
||
- 后续审计文档需要引用 P20 SPEC 当前状态
|
||
|
||
### 修改影响
|
||
|
||
| 选项 | 改动 | 工作量 |
|
||
|---|---|---|
|
||
| **a 纳入 F1-5 commit(我推荐)** | F1-5 同 commit 改 §6/§10/§11/§15 | 10 分钟(纯文档) |
|
||
| b 单独 commit | F1-5 后另起 commit "docs(spec): P20 同步 F1-5 落地" | 多一个 commit |
|
||
| c 留 Wave 5 文档收尾 | 30+ 天 SPEC 漂移,期间任何人看到 SPEC 都误判 | 高漂移风险 |
|
||
|
||
### 推荐选项
|
||
|
||
**a 纳入 F1-5 commit**
|
||
|
||
### 建议与理由
|
||
|
||
- SPEC owner 视角同步,不做的话 SPEC 永远和实际漂移
|
||
- 10 分钟工作量,不值得拆 commit
|
||
- 跟 D5(1 个 commit)相容
|
||
- 跟 F3-2C 的 ai-app-prompts.md A 处置同时改造 SPEC 一样的逻辑
|
||
|
||
**Neo 反馈**:
|
||
|
||
*同意 a 纳入*
|
||
|
||
---
|
||
|
||
## 总览决策表(Neo 自填)
|
||
|
||
| # | 决策项 | Claude 推荐 | Neo 决定 | 备注 |
|
||
|---|---|---|---|---|
|
||
| D1 | batch executor 方式 | Semaphore(3) + asyncio.gather + return_exceptions + ctx_snapshot | | |
|
||
| D2 | GUC C 方案处置 | 混合(纳入阶段 A) | | |
|
||
| D3 | 测试覆盖 | 纳入(阶段 A) | | |
|
||
| D4 | 走查 mock 授权 | 授权 PATCH 切 + 完成切回 | | |
|
||
| D5 | commit 拆分 | 1 个统一 commit | | |
|
||
| D6 | SPEC 同步 | 纳入 F1-5 commit | | |
|
||
|
||
---
|
||
|
||
## 已确认项(无需决策,记录在案)
|
||
|
||
| 项 | 决策 | 来源 |
|
||
|---|---|---|
|
||
| Q1 阶段 A+B 一起做 | ✅ 同意 | Neo 5/5 反馈 |
|
||
| Q4 子 1 — 不新建 sandbox_batch_runs 表,复用 sandbox_instance_id | ✅ 同意 | Neo 5/5 反馈 |
|
||
| Q4 子 3 — ai_run_logs 索引随 F1-5 一起补 | ✅ 同意 | Neo 5/5 反馈 |
|
||
| Q4 子 4 — BD_manual_biz_registry_tables.md 保持分离 | ✅ 同意 | Neo 5/5 反馈 |
|
||
|
||
---
|
||
|
||
## 关联
|
||
|
||
- F1-5 业务故事卡:[`00-W1-findings-stories.md`](00-W1-findings-stories.md)(F1-5 章节)
|
||
- F1-5 反馈响应:[`01-W1-findings-response.md`](01-W1-findings-response.md)
|
||
- P20 SPEC:[`docs/prd/specs/P20-runtime-context-sandbox.md`](../../prd/specs/P20-runtime-context-sandbox.md)
|
||
- W1-T7 PRD 批 1:[`docs/_overview/admin-api-prd/batch1-runtime-context-and-ai.md`](../admin-api-prd/batch1-runtime-context-and-ai.md)
|
||
- 调研子代理报告(本次会话内,未单独入仓)
|