# P0 文档冲突业务故事卡(8 条详版)

> 生成日期:2026-05-04 / 用途:把 `04-doc-conflicts.md` § 一 P0 表格里的 8 条冲突,扩展为业务人能看懂的"故事卡",每张卡含业务背景 / 矛盾点 / 影响传播 / 修改影响 / 多个推荐选项 / 建议判定。
> 阅读顺序:从 P0-1 到 P0-8 顺序读;同一条卡内按"业务背景 → 冲突逻辑 → 业务联系 → 修改影响 → 推荐选项 → 建议判定"。
> 决策方法:Neo 在每张卡的"推荐选项"中圈一个,或者补充自定义动作,本文不擅自做技术结论。
> 备注:本文不替代 04 索引,仅是 P0 八条的"业务化展开版"。详细判定四级(A 过期改文档 / B 现状对 / C 待补 / D Bug)定义见 04 文档头。

---

### P0-1. SPI 默认参数数量 26 vs 27

**关联页面/接口**:
- admin-web 路由:— (不直接展示)
- 小程序页面:`board-customer.ts`(SPI 子分图表渲染时会读 `cfg_index_parameters`)、`customer-detail.ts`(客户消费力等级展示)
- 后端 router:— (后端只调用 ETL Task,不直接读这张配置表)
- 数据库表/视图:`etl_feiqiu.dws.cfg_index_parameters`(过滤 `index_type='SPI'`)
- AI 应用 / ETL 任务:`SPI_Task`(消费力指数日刷新),`docs/specs/spi-spending-power-index/`

**业务背景**:
SPI(消费力指数,Spending Power Index)是给每个客户每天打分,用来回答"这个客户有多大消费能力、最近是不是在加快消费、稳定还是偶发"。这个分数会被任务系统用来决定"投入多大资源、用什么档位策略"。SPI 的算法不是写死的,而是把所有可调参数(权重、压缩基数、窗口天数、稳定性开关...)放在 `cfg_index_parameters` 配置表里,运维或算法工程师可以单独调权重而不动代码。两份文档对"配置表里 SPI 这一组到底有多少行参数"说法不一,这关系到"算法跑出来的分数是不是按预期的全集参数算的"。

**冲突逻辑**:
- 文档 A([`docs/prd/specs/00-数据依赖矩阵.md`](../prd/specs/00-数据依赖矩阵.md) §四 FDW 映射列表注释):写"cfg_index_parameters(含 SPI 26 个参数 ✅)"
- 文档 B([`docs/prd/specs/P2-etl-dws-miniapp-extensions.md`](../prd/specs/P2-etl-dws-miniapp-extensions.md) T3 已完成项):写"已完成,27 个参数含 Level/Speed/Stability 权重..."
- 现状(从 `docs/specs/spi-spending-power-index/requirements.md` AC1):未写明确数量,只说"包括窗口参数 / 金额压缩基数 / 子分权重 / 总分权重 / 映射与平滑参数",且提到"IF 缺失 THEN 用 DEFAULT_PARAMS 兜底",所以代码即使表里少 1 行也能跑

**业务联系**:
- 上游:这张表是 SPI 任务的唯一调参入口。如果某个参数因为种子脚本漏插入而走了代码兜底默认值,运维以为"调整表 = 调整算法"实际白调
- 下游:SPI 影响 task-list 的任务排序(P4 任务体系)、customer-detail 的消费力展示、board-customer 的 8 维度卡片之一。数字微调不影响大盘,但参数缺失会让"调参 → 失效"问题难定位

**修改影响**:
- 数据层:`cfg_index_parameters` 表内行数,可能要补 INSERT
- 接口层:无
- 展示层:无(展示口径不变)
- 工作量预估:小(< 1h),包括跑 COUNT、对照 SPEC 列表、补缺失行、修文档

**推荐选项**:
1. **选项 A 跑校验脚本**:在测试库执行 `SELECT param_key FROM dws.cfg_index_parameters WHERE index_type='SPI' ORDER BY param_key;`,把 27 项 SPEC 清单与实际逐行 diff,缺哪几行立即补 INSERT,把 00 数据依赖矩阵改为 27 → 优:一次性根治 劣:需要先停下来跑 SQL
2. **选项 B 信任 SPEC P2(改 00 矩阵)**:认定 P2 SPEC 后写,00 矩阵先写,直接把 00 改成 27 → 优:5 分钟改文档 劣:没核对实际表,如果实际是 26 还是错的
3. **选项 C 加 CI 校验**:写一个 `scripts/ops/verify_spi_params.py`,定期对照 SPEC 清单与表实际,差异时报警 → 优:长期保险 劣:本次冲突解决周期变长

**建议判定**:[C 待补],**理由**:文档说法不同 + 实际数据库需要校验,不能盲改任何一边

**Wave 验证锚点**:
- Wave 4 DWS/RLS 验证时会自然涉及 cfg_index_parameters 校验,可以并入那一步,本次先标记不动手


*反馈：这个问题需要你分2步解决。step1.深入调研：A)SPI的实现PRD。2)历史修改所涉及的Session。3)当前实现情况（如你说的选项A和C）。step2：在知道这个指数的目的的情况下，制定更新方案并实施，完成后更新所有每日的SPI参数，彻底收口此问题。*
---

### P0-2. 看板财务"确认收入"与 ETL"items_sum"口径关系未明示

**关联页面/接口**:
- admin-web 路由:— (admin-web 不直接展示门店财务)
- 小程序页面:`board-finance`(经营一览 → 客单价 / 日均额 显示)、`board-finance` § 经营一览 8 主指标
- 后端 router:`apps/backend/app/routers/board_finance.py`(读 `app.v_finance_daily_summary` / `app.v_finance_area_daily`)
- 数据库表/视图:`dws.dws_finance_daily_summary` / `dws.dws_finance_area_daily`,字段 `confirmed_income`、`gross_amount`、`items_sum`、`discount_total`
- AI 应用 / ETL 任务:`finance_daily_task.py`、`finance_area_daily.py`

**业务背景**:
门店看板要给店长展示"这家店今天/这周/这月赚了多少钱"。但飞球上游 `consume_money` 字段在不同时期口径不一致(三种历史口径混用),DWS 团队约定改用 `items_sum` 替代,定义为"五项费用之和:台桌费 + 商品费 + 陪打费 + 超休费 + 电费"。看板优化 PRD 又引入了一个新概念叫 `confirmed_income`(确认收入),用于客单价 / 日均额 等指标的分子。两个口径名字不一样,但都跟"门店收入"有关,一线运营如果不知道两者关系,看到客单价数据时就会困惑——"我门店一天收 1 万,客单价怎么只显示 80?(因为分母是开台数)"或者"为什么经营一览的发生额比客单价 × 单数高?"。

**冲突逻辑**:
- 文档 A([`apps/etl/connectors/feiqiu/CLAUDE.md`](../../apps/etl/connectors/feiqiu/CLAUDE.md) DWD 规则 1):"DWS 层及下游统一改用 `items_sum` = 台桌费 + 商品费 + 陪打费 + 超休费 + 电费"
- 文档 B([`docs/prd/2026-04-08__board-finance-optimization.md`](../prd/2026-04-08__board-finance-optimization.md) L80-L81):客单价 = `SUM(confirmed_income) / SUM(order_count)`,日均额同理用 `confirmed_income`
- 现状(从代码 grep `tasks/dws/finance_daily_task.py:250` 与 BD 手册 `BD_manual_dws_finance_daily_summary.md:148-149`):
  - `gross_amount = table_fee_amount + goods_amount + assistant_pd_amount + assistant_cx_amount`(注意:**不含** electricity_money,只有 4 项)
  - `confirmed_income = gross_amount - discount_total`(总和减去优惠)
  - `items_sum` 是 5 项(含 electricity_money),DWD 层概念,而 `gross_amount` 是 DWS 层的 4 项
  - 三者关系:`items_sum = gross_amount + electricity_money`、`confirmed_income = gross_amount - discount_total`

**业务联系**:
- 上游:DWS 任务已经按 4 项算 `gross_amount`、再减优惠得 `confirmed_income`,这是既成事实,不易改
- 下游:小程序 `board-finance.ts` 会展示客单价、日均额、发生额、确认收入多个指标,如果文档没说清三者关系,前端开发会反复问后端"分子取哪一个",QA 也无法判定数据是否正确

**修改影响**:
- 数据层:无 schema 变更
- 接口层:无契约变更
- 展示层:`board-finance.ts` 经营一览的字段说明文案可能需要补一行"客单价 = 确认收入 / 开台数"
- 工作量预估:小(< 1h),纯文档同步

**推荐选项**:
1. **选项 A 在 ETL CLAUDE.md 补三行口径关系**:把 `confirmed_income = gross_amount - discount_total`、`gross_amount = items_sum - electricity_money`、"客单价/日均额 用 confirmed_income"明文写进 ETL CLAUDE.md → 优:一处补全 劣:CLAUDE.md 行数会涨
2. **选项 B 在 DWS 权威规范集中说明**:在 `apps/etl/connectors/feiqiu/docs/database/DWS/main/BD_manual_dws_finance_daily_summary.md` 已有 L148-L149 描述,只需在小程序 board-finance api-audit 里加一段"指标对照表"链回这里 → 优:权威规范已有,只补链接 劣:维护两份文档
3. **选项 C 看板优化 PRD 加术语锚点**:在 `2026-04-08__board-finance-optimization.md` 顶部加"术语对照表"区块,列清 items_sum / gross_amount / confirmed_income 三者关系 → 优:看板维护者直接看 PRD 不用跨仓库 劣:这份 PRD 已经写完归档,改动会触发再次评审

**建议判定**:[C 待补],**理由**:三者关系文档没说,但代码已落地;补一段术语对照即可,无需改任何逻辑

*反馈：我理解这个问题是一个说明性的问题，我认为在涉及的前后端及数据库文档都应该留痕进行解释说明，也就是ABC选项都要做，我的理解对么？你的建议是什么？*
---

### P0-3. 4.1 财务看板 5 项 P2 修复是否阻塞 P11 上线

**关联页面/接口**:
- admin-web 路由:— (运维侧不参与)
- 小程序页面:`board-finance`(预收资产卡余额、首充续费指标、优惠占比、充值笔数、团购标签)
- 后端 router:`apps/backend/app/routers/board_finance.py`
- 数据库表/视图:`dws_finance_daily_summary`(卡余额快照字段)、`dws_recharge_daily`(首充续费)、`dws_groupon_*`(团购)
- AI 应用 / ETL 任务:相关 DWS Task

**业务背景**:
P11 是"上线部署"SPEC,意味着系统要正式给真实门店用。同时间财务看板优化 PRD(2026-04-08)记录了 5 项 P2 级 bug:① 卡余额快照在某些情况下不更新 ② 首充/续费指标全为 0 ③ 优惠占比计算口径有问题 ④ 充值笔数缺口 ⑤ 团购订单标签缺失。这 5 项是看板"看起来不准"的细节问题,但都是 ETL 层任务级 bug,不是前端样式。决策矛盾在于:P11 SPEC 的 AC6 写了"ETL 定时调度正常运行"——卡余额快照不更新算不算"调度正常"?如果算"运行就行,数据准确度后修",那可以先上线;如果算"运行 + 数据正确",那要先修完 5 项再上。这是上线门槛决策,不是技术决策。

**冲突逻辑**:
- 文档 A([`docs/prd/2026-04-08__board-finance-optimization.md`](../prd/2026-04-08__board-finance-optimization.md)):列出 5 项 P1/P2 修复,标 P2 但未与 P11 关联
- 文档 B([`docs/prd/specs/P11-deployment-launch.md`](../prd/specs/P11-deployment-launch.md) AC6):"ETL 定时调度正常运行" — 没说"调度结果数据正确"
- 现状:5 项 P2 暂未修复;P11 是否已开始上线流程文档未说

**业务联系**:
- 上游:无,这是产品决策不是依赖问题
- 下游:如果带 bug 上线,门店第一天就会发现"卡余额不对"、"团购订单分类错",信任度受损;如果先修再上,每项 1-3 天,5 项总计可能延期 1-2 周

**修改影响**:
- 数据层:5 项分别涉及不同 DWS Task 的口径修正
- 接口层:可能涉及字段补充(如 `is_groupon` 标签)
- 展示层:看板会"看起来变准",但同一字段名值会变
- 工作量预估:大(> 4h),实际是 5 个独立修复,每项 1-3h

**推荐选项**:
1. **选项 A 上线门槛 = 全部修完**:5 项全部修完才推 P11,目标"门店第一天看到的数据是准的" → 优:用户体验最好 劣:延期 1-2 周,P11 周边任务也卡住
2. **选项 B 上线门槛 = 调度跑 + 关键准确**:卡余额快照 + 团购标签必修(2 项),首充续费 / 优惠占比 / 充值笔数 接受"上线后第一周修复"挂横幅说明 → 优:权衡时效与质量 劣:用户看到部分指标会困惑,需要明显的"试运行"标识
3. **选项 C 上线门槛 = 调度跑就行**:5 项全部排到上线后修,在 P11 AC6 加注脚"数据准确度后续 P12 跟进" → 优:最快上线 劣:用户初次体验最差,可能伤害产品信任
4. **选项 D 拆 P11 阶段**:把 P11 拆 P11.1(调度运行)+ P11.2(数据校准),P11.1 先上,P11.2 跟进 → 优:阶段化,有里程碑 劣:文档要拆

**建议判定**:[C 待补],**理由**:这是产品决策不是技术冲突,只能 Neo 在体验/时效之间权衡

*反馈：我的反馈结论很明确，一定要满足数据准确的基本要求，再进行上线部署。另外，此项内容包括财务看板页的所有数据是否受到之后开发上线的时间沙盒模块的影响？逻辑是否调通有待进一步调研走查。*
---

### P0-4. 备注星星评分字段名 SPEC 2 字段 vs PRD 3 字段

**关联页面/接口**:
- admin-web 路由:— (后台不展示评分)
- 小程序页面:`task-detail.ts`(回访任务默认展开评分区域)、`notes.ts`(备注列表展示星星)
- 后端 router:`apps/backend/app/routers/notes.py`(创建 / 查询备注)
- 数据库表/视图:`biz.notes`,字段 `rating_service_willingness` smallint、`rating_revisit_likelihood` smallint
- AI 应用 / ETL 任务:应用 6 备注分析(注意:评分**不参与** AI 输入)

**业务背景**:
助教写完备注后,UI 默认在"回访任务"下展开一个 1-5 星打分区域,让助教快速记录"这个客户下次还愿不愿意再服务"和"再来店的可能性"。这两个分数是助教对客户的主观直觉,**不参与任务完成判定 / 不参与 AI 输入**,只在客户详情和备注列表里露出做趋势可视化用。SPEC 写了 2 个字段(意愿 + 再访),PRD 写了 3 个字段(意向 / 关系 / 服务)。问题在于:同一段功能不同文档写的字段数和字段名都不一样,前端 mock 和后端 schema 容易不一致,联调时会出 bug。

**冲突逻辑**:
- 文档 A([`docs/prd/specs/P4-miniapp-core-business.md`](../prd/specs/P4-miniapp-core-business.md) 备注星星评分):2 个字段 `rating_service_willingness`(再次服务意愿,1-5 可空)、`rating_revisit_likelihood`(再来店可能性,1-5 可空)
- 文档 B([`docs/prd/后端接口需求说明_数据需求PRD.md`](../prd/后端接口需求说明_数据需求PRD.md) 接口 I 与 J1 示例):3 项 `intention` / `relation` / `service`
- 现状(从 `db/zqyy_app/schemas/biz.sql` L226-L227 实际表):2 列 `rating_service_willingness smallint`、`rating_revisit_likelihood smallint`,确实是 2 字段

**业务联系**:
- 上游:数据库已经按 2 字段建表,迁移成本大
- 下游:小程序前端 mock 如果按 PRD 写了 3 字段,联调时会发现后端只接受 2 个,提交报错或多余字段被丢弃

**修改影响**:
- 数据层:无(表已经是 2 字段)
- 接口层:`POST /api/xcx/notes` 入参字段定义需要明确为 `ratingServiceWillingness` + `ratingRevisitLikelihood`(camelCase 对外)
- 展示层:`task-detail.wxml` 评分区域需要 2 颗星而不是 3 颗;`notes.wxml` 列表展示星星数量
- 工作量预估:小(< 1h),改 PRD 文档 + 前端 mock 字段名

**推荐选项**:
1. **选项 A 以 SPEC + 实际表为准,改 PRD**:`后端接口需求说明_数据需求PRD.md` 接口 I 与 J1 改成 2 字段名,与表对齐 → 优:符合"代码 / DB 是真理"原则 劣:需要找 PRD 维护人改文档
2. **选项 B 改表为 3 字段**:加列 `rating_intention`、`rating_relation`、把现有 willingness/revisit 拆 → 优:更细致 劣:涉及 schema 变更 + 数据迁移 + 业务上"3 个维度怎么定义"重新讨论
3. **选项 C 保留 2 字段表,但接口层 alias 双向支持**:后端 Pydantic alias `intention → service_willingness`、`relation → revisit_likelihood`、`service → ?`(第 3 个无对应不行) → 优:兼容旧 PRD 调用方 劣:第 3 字段无处放,逻辑别扭

**建议判定**:[A 过期-改文档],**理由**:数据库 + SPEC 一致是 2 字段,只有一份 PRD 写错,典型"PRD 落后于实施"

*反馈：选择A.你的判断没错，实施中对部分模块功能进行了适当的调整，PRD 落后于实施，*
---

### P0-5. dim_staff / dim_staff_ex 是否已建 app.v_* RLS 视图 + FDW 外部表

**关联页面/接口**:
- admin-web 路由:`/tenant-admins`(创建租户管理员时需要核对员工身份,可能间接用)
- 小程序页面:`apply.ts`(用户申请时,后端做"申请人 = 哪个员工"匹配)
- 后端 router:`apps/backend/app/services/matching.py`(员工匹配核心逻辑) → `apps/backend/app/routers/tenant_users.py`、`apps/backend/app/routers/tenant_excel.py`
- 数据库表/视图:`dwd.dim_staff` / `dwd.dim_staff_ex` 在 ETL 库;`app.v_dim_staff` / `app.v_dim_staff_ex` 在 ETL 库 RLS 视图;`zqyy_app.fdw_etl.*` 外部表(目前**未建**)
- AI 应用 / ETL 任务:无

**业务背景**:
小程序员工申请的时候要填"球房 ID + 手机号 + 编号 + 昵称",后端拿到后要回查"飞球数据里这个手机号或编号对应哪个员工",找到后绑定 user_id ↔ staff_id。员工信息在 ETL 库 `dwd.dim_staff`(员工主表)+ `dwd.dim_staff_ex`(扩展信息),按"双 schema"规则需要在 ETL 的 `app.v_dim_staff` / `app.v_dim_staff_ex` 同时建 RLS 视图,然后还要在业务库 `zqyy_app.fdw_etl` 建外部表把视图映射进来,后端通过 `fdw_etl.v_dim_staff` 查询。问题在于:文档说"已建",但实际能不能查到不知道。

**冲突逻辑**:
- 文档 A([`docs/prd/specs/P3-miniapp-auth-system.md`](../prd/specs/P3-miniapp-auth-system.md) AC7):用户申请同时匹配 `dwd.dim_assistant`、`dwd.dim_staff`、`dwd.dim_staff_ex`
- 文档 B([`docs/prd/specs/00-数据依赖矩阵.md`](../prd/specs/00-数据依赖矩阵.md) §四):新增 FDW 映射员工信息表
- 现状(从代码 grep):
  - `db/etl_feiqiu/schemas/app.sql` L195、L219 已建 `app.v_dim_staff` 与 `app.v_dim_staff_ex`(ETL 库 RLS 视图存在)
  - `db/fdw/setup_fdw.sql` 不含 dim_staff(业务库 FDW 外部表**未建**)
  - `apps/backend/app/services/matching.py:128` 注释 "fdw_etl.v_dim_staff/v_dim_staff_ex → app.v_dim_staff/v_dim_staff_ex" 暗示后端已经改为直连 ETL 库的 `app.v_dim_staff`,**绕过 FDW**

**业务联系**:
- 上游:小程序申请走 P3 SPEC 的匹配链,如果 ETL 库 RLS 视图已建可正常工作
- 下游:租户管理员后台的"用户审核"可能也复用 matching.py;tenant_users.py、tenant_excel.py 都引用了 dim_staff

**修改影响**:
- 数据层:决定补 FDW or 维持直连两条路
- 接口层:matching.py 已直连 ETL 库,无需改;如果改为走 FDW,需要回退代码
- 展示层:无
- 工作量预估:小(< 1h),改 setup_fdw.sql 加 2 张外部表 + 在测试库回放;或者只更新文档说明"matching 走直连不走 FDW"

**推荐选项**:
1. **选项 A 维持直连不补 FDW,只更新文档**:matching.py 已经直连 `app.v_dim_staff`,符合"业务库通过 FDW 只读访问 ETL"约束的精神(连的是 ETL 库的 app.v_*,数据库进程是 ETL),把 00 数据依赖矩阵改为"dim_staff 通过后端直连 ETL,不走 FDW" → 优:零代码改动 劣:与"业务库通过 FDW 访问 ETL"通用规则有偏差,需要文档说明例外
2. **选项 B 补全 FDW 外部表,统一走 FDW**:在 `db/fdw/setup_fdw.sql` 加 2 张外部表,把 matching.py 改回 `fdw_etl.v_dim_staff` → 优:统一规则 劣:涉及测试库回放 + 后端代码回退
3. **选项 C 保留双通道**:matching 走直连,其他可能用到的地方走 FDW,在两边都做配置 → 优:灵活 劣:维护双重映射混乱

**建议判定**:[C 待补],**理由**:决策需要 Neo 拍"是否要 FDW 统一"的产品规则,代码已经有方案

**Wave 验证锚点**:
- Wave 4 RLS 双 schema 校验时会列出 app schema 全部视图,可顺便核对 dim_staff 是否已建


*反馈：我选择偏向 选项 B 补全 FDW 外部表,统一走 FDW的方案。原因是要保证项目工程的规范性与合理性。但这里有个问题，需要你深入调研后，再决定具体方案：推测为什么当时进行了这些修改？推测这个功能为什么没有进行符合工程规范的架构设计？*
---

### P0-6. clearAllTasks 高危操作无运行模式守卫

**关联页面/接口**:
- admin-web 路由:`/task-engine/trigger-jobs`(顶部 danger 按钮)
- 小程序页面:无
- 后端 router:`apps/backend/app/routers/admin_task_engine.py:570 DELETE /api/admin/task-engine/clear-all-tasks`
- 数据库表/视图:`biz.coach_tasks`、`biz.coach_task_history`、`biz.coach_task_transfer_log`、`biz.notes`(关联清理)
- AI 应用 / ETL 任务:无

**业务背景**:
admin-web 的"小程序任务管理 → 定时任务"页有一个顶部 danger 按钮"清空所有助教任务",一键 DELETE 全部 `biz.coach_tasks` + 关联的转移日志 + 历史 + 备注。本意是开发/测试阶段重置数据让 task_generator 重新跑一遍,但目前**没有任何运行模式守卫**:无论生产还是沙箱,只要登录的是 super_admin 都能点。NeoZQYY 已经有"runtime context / sandbox 模式"机制(P0-7 详),理论上"清空"操作应该只在 sandbox 才允许。这是个真 Bug,不是文档冲突。

**冲突逻辑**:
- 文档 A(`02b-adminweb-page-matrix.md` § 五-7):"`clearAllTasks` 注释写'测试用',现状无运行模式守卫"
- 文档 B(后端代码 [`apps/backend/app/routers/admin_task_engine.py:573-577`](../../apps/backend/app/routers/admin_task_engine.py)):docstring 写"【测试用】清空所有 coach_tasks 及关联数据(仅超级管理员)。用于开发/测试阶段重置任务数据"
- 现状(从代码 grep `apps/backend/app/routers/admin_task_engine.py:579`):仅 `_require_super_admin(user)` 角色守卫,**没有 sandbox / 运行模式判断**

**业务联系**:
- 上游:运维场景 super_admin 在生产环境误点(或者测试人员账号在生产环境登录),后果是当日全部门店的助教任务被清空,task_generator 下次跑(每日 04:00)才重建,中间 N 小时所有助教看到任务列表为空
- 下游:如果生产门店上线后这种事故发生,直接影响门店当天运营,信任度伤害大

**修改影响**:
- 数据层:无
- 接口层:`DELETE /api/admin/task-engine/clear-all-tasks` 增加守卫,非 sandbox 模式下返回 403
- 展示层:admin-web 的 `TriggerJobs.tsx` 顶部按钮在 live 模式下置灰 + Tooltip"仅 sandbox 可用"
- 工作量预估:小(< 1h),后端加 5 行 + 前端按 runtime_context 判断 disabled

**推荐选项**:
1. **选项 A 后端硬守卫**:在 `clear_all_tasks` 函数里读 `meta.runtime_context` 当前 site 的 mode,非 sandbox 直接 403;前端按钮按 runtime context 联动 disabled → 优:双重保险,即使 admin-web 没改前端,后端也兜底 劣:跨门店时如果 super_admin 的 site_id 上下文不明确,需要先选门店再清(改交互)
2. **选项 B 仅前端守卫**:在 `TriggerJobs.tsx` 按 runtime_context 判断 disabled → 优:小改 劣:绕过前端直接 curl DELETE 还能成功,治标不治本
3. **选项 C 改成"按 site_id 清空"+ 二次确认弹窗输入门店简称**:不一刀切清全部,而是要求点击者输入要清的门店 site_code 二次确认,即使生产误点也只影响一家门店 → 优:即使 sandbox 误操作也限在单店 劣:语义变了,原"清空所有"功能消失
4. **选项 D 直接删除按钮 + 接口**:这个功能本来就是测试用,生产不需要,改成只能通过 `scripts/ops/` 命令行执行 → 优:最安全 劣:测试场景便利性下降

**建议判定**:[D Bug],**理由**:文档明文标"测试用"但代码没守卫,这是实现与设计不一致

*反馈：我偏向选项C，正如你所说，这是一个开发测试用的功能，我当前需要这个功能，只不过时间沙箱上线后，这个清除可能包含2个意思：全局和沙箱内，这个是需要确认的，以及门店的二次确认，这个根据沙箱和门店维度重新设计，但功能我确认是有保留价值。*
---

### P0-7. Runtime Context / 虚拟时间无独立 SPEC

**关联页面/接口**:
- admin-web 路由:`/settings/runtime-context`(切 live ↔ sandbox + 历史日期选择)
- 小程序页面:`runtime-clock.ts`、`customer-records.ts`(读 `business_year/month` 显示)、跨店切换
- 后端 router:`apps/backend/app/routers/admin_runtime_context.py`、`apps/backend/app/routers/xcx_runtime_clock.py`
- 数据库表/视图:`meta.runtime_context`(从迁移 `db/zqyy_app/migrations/20260501__runtime_context_sandbox.sql` 推断)
- AI 应用 / ETL 任务:多个 AI 提示词文件已读 runtime_context(`app5_tactics_prompt.py`、`app6_note_prompt.py`、`app7_customer_prompt.py`)

**业务背景**:
"沙箱"是给开发/演示/历史回放用的:把"现在的时间"虚拟为某个历史日期,后端 task_generator 按那天的数据生成任务,小程序看到的也是那天的客户列表和金额。比如演示给老板看 2026-03-01 的任务派发,可以把 sandbox 时间设成 2026-03-01,所有逻辑都按那天算。这个机制涉及小程序 + 后端 + AI 提示词 + ETL 影子跑数,改动面广,但目前 `docs/prd/specs/` 下**没有独立 SPEC**,只能从代码反推。Wave 1 走查、Wave 4 验证时如果遇到"沙箱模式下某指标算错了",没有判据可以查"应该是什么样"。

**冲突逻辑**:
- 文档 A(根 [`CLAUDE.md`](../../CLAUDE.md) 历史追溯):"2026-04-15~05-02 累积变更基线 — AI 重构 + Runtime Context + DWS 修复"
- 文档 B(`docs/prd/specs/`):无独立 Runtime Context SPEC
- 现状(从代码 grep):
  - `apps/backend/app/routers/admin_runtime_context.py` 已实现完整接口(GET sites / PATCH 切换)
  - `meta.runtime_context` 表存在(从迁移 `20260501__runtime_context_sandbox.sql`)
  - `apps/miniprogram/miniprogram/utils/runtime-clock.ts` 提供 `getBusinessClock()`
  - 多处 AI 提示词已读这个上下文
  - 02b-adminweb 矩阵 §3.15 描述了 admin-web 操作面

**业务联系**:
- 上游:几乎所有读"当前时间"的逻辑都依赖 runtime context;无 SPEC 意味着新加功能时不知道是否要支持沙箱
- 下游:Wave 1 全链路走查时,如果发现"某 AI 应用在 sandbox 模式输出了 live 数据",没有 SPEC 判定到底是 bug 还是设计如此

**修改影响**:
- 数据层:无新表,只是补 SPEC 文档
- 接口层:无,只是文档化已有接口契约
- 展示层:无
- 工作量预估:中(1-4h),需要把 admin-web、后端、小程序、AI 提示词四端的实现读一遍写成统一 SPEC

**推荐选项**:
1. **选项 A 补一份 PX-runtime-context.md**:在 `docs/prd/specs/` 加 `PX-runtime-context.md`(暂定 P20 编号),写清表结构、接口契约、各端读取方式、与 ETL 影子跑数的衔接 → 优:有判据,后续走查可对照 劣:需要 1-3h 写 SPEC + 读代码反推
2. **选项 B 在现有 SPEC 加章节**:把 runtime context 章节加到 `P19-index-backtest-task-simulation.md`(任务模拟与回测)或新加 `P11.x` → 优:不新增文档 劣:Runtime Context 是横切关注点,塞进任一现有 SPEC 都不顺
3. **选项 C 链接到审计记录**:在 CLAUDE.md 加锚点指向 `docs/audit/changes/2026-04-15` ~ `2026-05-02` 的审计 → 优:零工作量 劣:审计记录是变更日志不是规范,后人无法快速理解全貌
4. **选项 D 暂不补 SPEC,Wave 4 走查时再写**:把这条挪到 Wave 4 的待办,走查时一边读代码一边写 SPEC → 优:走查与文档化合一 劣:Wave 1-3 期间没有判据

**建议判定**:[C 待补],**理由**:四端代码都已实现,缺的只是文档化,补 SPEC 是确定性工作

*反馈：按照Session和当前实现（代码和数据库数据）补一份文档，且需要对这个时间沙箱功能继续深入调研和测试，因为涉及内容和模块太多了，远没有做到完成和收口的地步*

---

### P0-8. DBViewer POST /db/query 是否仅允许 SELECT,前端无校验

**关联页面/接口**:
- admin-web 路由:`/logs/db-viewer`(右上 SQL 编辑器 + 执行按钮)
- 小程序页面:无
- 后端 router:[`apps/backend/app/routers/db_viewer.py:152 POST /api/db/query`](../../apps/backend/app/routers/db_viewer.py)
- 数据库表/视图:连接 ETL 库只读连接 `get_etl_readonly_connection(user.site_id)`(走 RLS 视图)
- AI 应用 / ETL 任务:无

**业务背景**:
admin-web 提供了一个"数据库查看器"页面,运维直接写 SQL 在前端文本框 → 提交到后端 → 返回结果展示。设计意图是"只读 SELECT 探查 ETL 库",但前端文本框没有任何校验,如果后端拦截不严,理论上可以执行 DDL(CREATE / ALTER / DROP)甚至 GRANT。这关系到生产环境的数据库安全。

**冲突逻辑**:
- 文档 A(`02b-adminweb-page-matrix.md` § 五-6):"`POST /db/query` 后端是否仅允许 SELECT?前端无校验,依赖后端 sqlparse 或类似拦截"
- 文档 B(后端实现 [`apps/backend/app/routers/db_viewer.py:171-176`](../../apps/backend/app/routers/db_viewer.py)):`_WRITE_KEYWORDS = re.compile(r"\b(INSERT|UPDATE|DELETE|DROP|TRUNCATE)\b", re.IGNORECASE)` — 只拦截 5 个关键词
- 现状(从代码 grep `apps/backend/app/routers/db_viewer.py:37-40`):
  - 拦截:`INSERT / UPDATE / DELETE / DROP / TRUNCATE`(5 个)
  - **未拦截**:`ALTER`(改表结构)、`CREATE`(建表/视图/函数)、`GRANT` / `REVOKE`(权限)、`COPY`(批量导入)、`CALL`(存储过程)、`COMMENT`(注释,无害但属 DDL)
  - 已有保护:30s 超时、1000 行返回上限、`get_etl_readonly_connection` 推测使用只读账号(需进一步验证)
  - 风险:如果只读账号其实有 DDL 权限,可以执行 `CREATE TABLE` / `ALTER TABLE`;如果是真只读账号,这些命令也会被 PG 拒绝,但错误信息可能泄露内部 schema

**业务联系**:
- 上游:这个工具被运维和开发使用,谁都不会主动写 DROP,但写错 SQL 触发 ALTER 或者复制粘贴别人脚本时混入这些关键词,就可能改库
- 下游:如果只读账号配置错(被分配了 DDL 权限),严重时可能在生产环境改 schema

**修改影响**:
- 数据层:无
- 接口层:`db_viewer.py` 拦截规则升级
- 展示层:可选给 admin-web SQL 编辑器加 lint 提示
- 工作量预估:小(< 1h),后端关键词正则补全 + 加白名单校验"必须以 SELECT / WITH / SHOW / EXPLAIN 开头"

**推荐选项**:
1. **选项 A 改为白名单**:从黑名单改成白名单,要求 SQL 第一个有效关键词必须是 `SELECT` 或 `WITH ... SELECT` 或 `EXPLAIN` 或 `SHOW`,其他一律 400 拒绝 → 优:正面校验最稳健 劣:需要简单 SQL 解析(去注释、去空白)
2. **选项 B 黑名单扩展**:在 `_WRITE_KEYWORDS` 加 `ALTER|CREATE|GRANT|REVOKE|COPY|CALL|COMMENT`,继续黑名单模式 → 优:改动最小 劣:黑名单永远不全,新版 PG 出新关键词需要追
3. **选项 C 数据库账号兜底**:核对 `get_etl_readonly_connection` 用的账号是否真只读(`pg_user.usesuper=false`、无 INSERT/UPDATE/DELETE/DDL 权限),后端代码不动 → 优:防御深度,即使代码漏了 SQL 关键词也无效 劣:如果账号配置漂移(运维不小心 GRANT 了),无人察觉
4. **选项 D 同时做 A + C**:白名单代码 + 只读账号双保险 → 优:最稳 劣:工作量大一点

**建议判定**:[D Bug],**理由**:实现与设计意图不一致(设计是"只读 SELECT",实现是"黑名单 5 个关键词"),需要修代码

*反馈：选项D*
---

> 共 8 条 P0 卡片,本文不替代 04 索引,仅做业务化展开。每张卡的"建议判定"是产出方建议,以 Neo 最终拍板为准。
