Files
Neo-ZQYY/docs/_overview/04a-feedback/P0-7-runtime-context-todos.md
Neo 509cf43284 chore(docs): Wave 0 调研产出 + P0/P1/P2 反馈调研
建立项目级标杆文档 docs/_overview/ 作为产品全景索引,
解决"PRD 零碎、文档膨胀、跨子系统调研无入口"的问题。

主要内容:
- 00-index 总索引 + 维护协议 + 与 CLAUDE.md 关系
- 01-product-overview 产品全景脑图(6 角色 / 6 子系统 / 数据流 /
  7 业务概念 / 8+1 AI 矩阵 / 22 术语)
- 02a-miniprogram-page-matrix 小程序 21 页业务指纹
- 02b-adminweb-page-matrix admin-web 19 路由业务指纹
- 03-test-spec 测试规范 (L1-L5 分层 + 走查模板 + 75-95 case 估算)
- 04-doc-conflicts 39 条冲突索引(P0×8 / P1×13 / P2×13 + 5 子项)
- 04a/b/c-conflicts-*-detail 业务故事卡(7 字段:关联/逻辑/影响/选项/判定)
- 05-orphan-pages-cleanup admin-web 6 孤儿页面处置(1 归档 + 4 保留)
- WAVES-MASTER-PLAN.md 全 Wave 主计划(0-5,共 22-32 工作日)
- WAVE-1-KICKOFF.md Wave 1 实施 kickoff
- GLOBAL-DECISION-DASHBOARD.md 全局决策仪表板

反馈调研产物:
- 04a-feedback/ P0 两轮反馈(8+8 项决策 + D-1/2/3 + F-1/2 子代理产出)
- 04b-feedback/ P1 两轮反馈(13+1+5 项 + E-1/2/3/4 + G-1/2 子代理产出)
- 04c-feedback/ P2 反馈(13 项 + 5 子项 + H-1/2/3 子代理产出)
- NEO-DECISIONS-LOG 累积决策记录

关键追加发现 8 处 D Bug(原蓝本 0):
- P0-3 看板沙箱接入(Wave 1 W1-T1)
- P0-5 致命 1 (4 处 fdw_etl 残留, 已修 commit 17f045a)
- P0-5 致命 2 (JWT aud 缺失, 已修 commit 17f045a)
- P0-6 clearAllTasks 守卫 (Wave 3)
- P0-8 DBViewer 黑名单漏 (已修 commit 17f045a)
- P1-3 task-detail 跳转传 task_id 而非 customer_id
- P2-7 board-finance 隐式 null
- 2 个独立 Bug (page_context.created_at + ClueCategory 字典)

参考: docs/_overview/00-index.md
2026-05-04 07:38:28 +08:00

14 KiB
Raw Blame History

Runtime Context 深入调研 / 测试 / 收口建议

日期2026-05-04 / 触发Neo 反馈"涉及内容多,远未做到完成和收口的地步" 关联 SPECdocs/prd/specs/P20-runtime-context-sandbox.md 关联审计:docs/database/changes/2026-05-01__runtime_context_sandbox.md 与 5 份 2026-05-02__sandbox_*.md


一、调研中发现的"未完成 / 未验证"项

按优先级分类。P0 = 上生产前必须收口P1 = 灰度期间必须验证P2 = 长期改进

P0上生产前必须收口

  1. 生产库迁移未执行db/zqyy_app/migrations/20260501__runtime_context_sandbox.sqldb/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql 仅在 test_* 库执行;zqyy_app 生产库与 etl_feiqiu 生产库未跑。回滚 SQL 已附在迁移末尾,但生产环境的回滚演练未做。
  2. 多门店并行 sandbox 未跑过验证:架构支持,但端到端验证脚本只覆盖单 site2790685415443269。site A 切 sandbox + site B 保持 live 时,下列点未验证:
    • site B 用户登录看到的 business_date 是否仍是真实日
    • site B 的 ETL _fdw_context 查询是否未受 site A GUC 污染GUC 是连接级,但连接池复用是否带来串扰未测)
    • site B 的 AI cache 是否未读到 site A 的 sbx_*: 命名空间数据
  3. BD_Manual 与现状不一致未修订docs/database/BD_Manual_runtime_context_sandbox.md § 3.3 / § 4 仍写 "切沙箱按 site_id 暂停 biz.trigger_jobspaused_by_sandbox";代码已移除此逻辑(见 admin_runtime_context.pybiz_triggers_unchanged step。文档与代码冲突会误导运维。
  4. Steps key 与文档不一致2026-05-02__sandbox_admin_web_manual_checklist.md 第 1 节列出的期望步骤 keyruntime_context_upserted / pending_jobs_cancelled / runtime_cache_purged)与代码实际产出的 keycancel_etl_processes / cancel_task_queue / cancel_ai_runtime / cancel_ai_jobs / biz_triggers_unchanged / apply_context)不匹配,手工清单需要修订。
  5. coach_service._build_task_groups 是否带 site_id + runtime_filter 未确认2026-05-02__sandbox_no_future_data_plan.md 标注 "550-566 未带 site_id 过滤 + runtime filter",状态为 " 已实施",但 grep task_runtime_filtercoach_service.py 中未匹配到此函数路径。需重新核对。

P1灰度期间必须验证

  1. 小程序覆盖不全board-finance.ts / board-customer.ts / board-coach.ts 是沙箱"不看未来"主受益方,但 grep runtime-clock 在这 3 个文件未匹配到。设计文档要求改 isCurrentMonthFilter,未在代码层确认。
  2. AI App8 consolidate 未确认grep 显示 App2/2a/3-7 都接入了 as_runtime_business_now_str,但 app8_consolidate_prompt 未在结果中。如果 App8 在 sandbox 模式输出"今天"是真实日,会破坏一致性。
  3. ai/dispatcher.pyai/admin_service.py 接入状态模糊plan 文档 § B1 提到 dispatcher.py 259/330 去重键应改为 as_runtime_today_paramadmin_service.py 86-87 等 AI 调用统计窗口标 "需产品确认"。grep 结果未确认是否落地。
  4. 跨页时间漂移:小程序 runtime-clock.ts 60s in-memory 缓存。从 page A 切到 page B 时,是否一定 force=true 重拉?切沙箱后切回 live 时,缓存是否被 clearBusinessClockCache 主动失效grep 未发现任何调用 clearBusinessClockCache 的位置。
  5. task_generator 部分 SQL 业务日上界未单测覆盖plan 文档 § B1 标 873 行 "dwd.dim_assistant 直连,非 RLS 视图,需切换 app.v_dim_assistant"status 是否落地未单测。
  6. page_context.py 7 处直连 ETL 依赖 GUC 兜底:未单独传 ref_date;如果 _fdw_context 之外的连接路径调用了 page_contextGUC 不会下发。
  7. AI cache 命名空间清理逻辑sandbox 模式下 target_idsbx_*: 前缀,但是否有清理脚本删除已结束沙箱实例的 cache 行未实施。长期使用会膨胀 biz.ai_cache
  8. 沙箱写入数据归零脚本缺失coach_tasks / coach_task_history / recall_events / ai_cache / ai_run_logs / ai_trigger_jobs 在 sandbox 长期使用后会积累大量 sandbox_instance_id 行;清理脚本(按 runtime_mode='sandbox' AND sandbox_instance_id=... 限定未实施BD_Manual § 6 仅警告"不要直接清空"。

P2长期改进

  1. DIM SCD2 维度沙箱回放v_dim_assistant / v_dim_member / v_dim_member_card_account / v_dim_staff / v_dim_staff_ex / v_dim_table 保留 scd2_is_current=1 当前快照;如需"sandbox 当时维度状态"需把 WHERE 改为 scd2_start_time <= bd AND (scd2_end_time > bd OR is null)。但这会让一行变多行,影响 JOIN需重构。
  2. 配置表 SCD2 时间戳"未来"边界v_cfg_assistant_level_price 等用 effective_from <= bd AND effective_to >= bd 双向夹住,但 effective_to 默认值是否合理(9999-12-31 vs NULL未在 SPEC 中固化。
  3. utils/time.ts 相对时间是否按沙箱算plan § B2 明确不改;但 sandbox 演示"3 天前"按真实日推算可能出现不直观的标签(如 sandbox=2025-09-01真实"3 天前"是 2026-05-01。需确认是否影响演示体验。
  4. ai_mode 字段实际未使用:表与代码都保留 ai_mode='live'CHECK 约束限定只能 live。如未来要做"沙箱不真实调 DashScope改返 mock"需移除 CHECK + 实现 mock dispatch预留扩展点未明确。
  5. 业务日跨日切换边界RuntimeContext.business_date 包含 "凌晨小于 BUSINESS_DAY_START_HOUR 算昨天" 的逻辑sandbox 模式下也按这个规则推算 business_now。是否符合演示意图?演示者通常希望 sandbox_date 全天 24h 都是同一天。需评估。
  6. RuntimeContext 表无审计日志:切换历史只能通过 updated_by + reason + updated_at 看最后一次状态,无完整 transition log 表。如需追溯"该 site 历史上哪天进过 sandbox",无数据。
  7. 测试用例缺位:仓库内未发现 tests/ 下 runtime_context 相关 pytest 文件。当前所有验证依赖 tools/db/verify_sandbox_end_to_end.py 一次性脚本,未纳入 CI。

二、跨模块影响清单(可能漏处理的地方)

模块 是否读 RuntimeContext 测试覆盖 风险
后端 task_enginetask_manager / task_generator / task_expiry 自动化 PASS
后端 board_service / coach_service / customer_service 自动化 PASS部分 中:coach_service._build_task_groups 路径未确认
后端 fdw_queries GUC + helper 自动化 PASS
后端 ai/cache_service / run_log_service 自动化 PASS
后端 ai/dispatcher / admin_service 不确定 未测 中:去重键、统计窗口
后端 ai/prompts/app2 / app2a 自动化 PASS
后端 ai/prompts/app3-7 自动化 PASS仅 current_time
后端 ai/prompts/app8_consolidate 不确定 未测
后端 ai/data_fetchers/page_context 是(部分依赖 GUC 自动化 PASS 中:连接路径外的边界
后端 routers/tenant_users 未测
后端 routers/admin_runtime_context Playwright PASS
后端 routers/xcx_runtime_clock 自动化 PASS
ETL 库 39 个 RLS 视图 C 方案 GUC 自动化 PASSmax 截断)
ETL task_engine / flow_runner 否(直接读 ODS/DWD 低(设计有意)
admin-web RuntimeContext.tsx Playwright PASS
admin-web 其他页AIRunLogs / TriggerManager / AIDashboard 间接 Playwright PASS
admin-web 其他 AI 页AIOperations Card 1 / 4 间接 未在 UI 触发(成本高)
小程序 performance / performance-records / task-list / customer-records / customer-service-records grep PASS
小程序 board-finance / board-customer / board-coach 不确定 未确认 :沙箱主受益面
小程序 customer-detail / chat / utils/time.ts 否(设计共识)
tenant-admin 低(不展示业务数据)
MCP server 不确定 未测 低(只读,但若读到 sandbox 数据需评估)

三、Wave 1 走查时必测的场景

  1. live → sandbox 单门店切换流程admin-web 切到 sandbox=2025-09-01确认 6 个 step 全 success表格更新DB 状态正确。
  2. sandbox → live 还原:切回后 sandbox_date / sandbox_instance_id 恢复 NULL写入恢复 ('live','live')
  3. 小程序业务时钟一致性:登录小程序,进入 5 个已接入页面,确认 getBusinessClock() 返回的 business_date = 2025-09-01;切回 live 后再进同一页,业务日变回真实日。
  4. 小程序板看不看未来:进入 board-finance / board-customer / board-coach,确认看到的最大日期 ≤ sandbox_date这是当前覆盖率最低的部分)。
  5. AI 应用在 sandbox 模式输出admin-web AIOperations 触发一个 App3成本最低等待执行后到 AIRunLogs Drawer 看 current_time 字段是否为 2025-09-01 HH:MM
  6. AI cache 命名空间隔离:触发同一 member_id 的 App7 在 sandbox + live 各一次DB 查 biz.ai_cache.target_id 应有两行sandbox 行带 sbx_*: 前缀。
  7. 任务表 runtime 维度共存:手动触发 task_generatorDB SELECT runtime_mode, sandbox_instance_id, COUNT(*) FROM biz.coach_tasks GROUP BY 1,2,确认 live 与 sandbox 两套并存。
  8. 触发器调度不暂停/triggers?tab=all 12 条触发器全 enabledpaused_by_sandbox
  9. AIDashboard 真实日期:切沙箱后 AIDashboard "今日" 仍然是真实日(如 0 调用,因为今天确实未触发),不被拉到 sandbox_date
  10. 多门店并行site A sandbox + site B live:用 site B 用户登录小程序,确认 business_date 仍是真实今天,业务数据未被截断。
  11. 沙箱期 ETL 跑批不污染演示sandbox 模式下让 ETL 跑一批真实数据进 DWS写真实最新日小程序板看 max 日期仍是 sandbox_date(因 RLS 视图截断)。
  12. 回滚演练:在 test 库跑迁移末尾的回滚 SQL确认 7 张表的列被 DROP、唯一索引恢复旧版、site_runtime_context 表 DROP后端代码降级 livefail-soft

四、推荐补充测试用例(给 Wave 1 用)

# 场景 测试方式 期望
T1 sandbox 边界:"今天"切 sandbox 到今天 PATCH mode=sandbox, sandbox_date=today 422 拒绝("sandbox_date 不能晚于真实今天"含等号?需确认;当前代码 >,等号允许)
T2 sandbox 切到极早历史日2020-01-01 PATCH 切换 接受;验证视图无数据(早于 ETL 范围)
T3 reset_sandbox=false 沿用实例 第二次切沙箱不重置 sandbox_instance_id 保持不变
T4 未审核小程序用户访问 /api/xcx/runtime/clock curl with limited token 403 / 401
T5 非 super_admin 访问 admin runtime API curl 403
T6 _fdw_context GUC 串扰 同连接池循环切 site 每次 SET LOCAL 不溢出
T7 runtime-clock 缓存失效 切 sandbox 后立即在小程序内观察 5 个页面 60s 内可能仍是旧值;切换流程是否需要主动 push 给小程序未实现
T8 AI dispatcher 去重键 同 member_id 在 live + sandbox 各触发一次 App3 不应去重,两个独立 trigger_job
T9 沙箱产生大量数据后清理 INSERT 1000 条 sandbox 任务,运行清理脚本 仅 sandbox 行被删live 不受影响(清理脚本待实施
T10 app.business_date_now() 在多事务并行下的 STABLE 行为 并发查询 39 视图 同事务内值固定,不同事务独立
T11 effective_from / effective_to 双向夹住后 v_cfg 视图返回 0 行边界 sandbox=2020-01-01 看 v_cfg_assistant_level_price 期望返回当时生效档位(依赖测试库种子数据)
T12 DWS 物理跑批进 sandbox 期 sandbox=2025-09-01 时让 ETL 跑入 2026-05-04 数据 视图侧仍截断到 2025-09-01

五、收口路径建议

step1 完成本次 SPEC 起草(已落 docs/prd/specs/P20-runtime-context-sandbox.md
   ↓
step2 修订 BD_Manual + manual_checklist 以匹配代码现状P0-3 / P0-4
   ↓
step3 跨模块缺口扫描:补 board-finance/customer/coach + dispatcher + admin_service + app8P1-6/7/8
   ↓
step4 多门店并行 sandbox 端到端脚本P0-2
   ↓
step5 沙箱写入清理脚本 + cache 命名空间清理P1-12 / 13
   ↓
step6 Wave 1 走查(按 §三 12 项 + §四 12 用例)
   ↓
step7 走查发现 bug → 修 bug → SPEC § 13 「已知冲突」更新 → todos 项标 done
   ↓
step8 生产库灰度执行迁移(先 test_etl_feiqiu / test_zqyy_app 二跑,再 zqyy_app + etl_feiqiu
   ↓
step9 SPEC 进入 v1.1,关掉 P0-7 反馈

六、关联资产

  • SPECdocs/prd/specs/P20-runtime-context-sandbox.md
  • 主迁移:db/zqyy_app/migrations/20260501__runtime_context_sandbox.sql
  • ETL RLS 视图迁移:db/etl_feiqiu/migrations/20260502__rls_views_business_date_upper_bound.sql
  • BD_Manualdocs/database/BD_Manual_runtime_context_sandbox.md待修订
  • 6 份 changesdocs/database/changes/2026-05-01__runtime_context_sandbox.md + 2026-05-02__sandbox_*.md
  • 端到端验证:tools/db/verify_sandbox_end_to_end.pytools/db/verify_admin_web_sandbox.py
  • 后端核心:apps/backend/app/services/runtime_context.py
  • admin-web 入口:apps/admin-web/src/pages/RuntimeContext.tsx
  • 小程序入口:apps/miniprogram/miniprogram/utils/runtime-clock.ts