## [ERR-20260429-010] data-quality-check.ts 静默吞错导致假阳性"数据缺失"

**日期**: 2026-04-29
**类别**: 脚本设计反模式 / 沉默失败

### 现象

PR 合 develop → staging 后 deploy-uat.yml 的 post-deploy-regression job 失败：
```
❌ [performance] 权限种子已存在
  permissions 表中无 'performance:%' 权限码
⚠️ [performance] DB schema FK 约束
  未找到匹配的 DB schema（尝试了: performance, platform_performance）
```

UAT 数据看起来缺一大堆。但实际生产/UAT 库**完全正常**，performance schema 真叫 `platform_performance`，权限种子也有。

### 根因

脚本第 48 行默认 DB_CMD：
```ts
const DB_CMD = process.env.DB_CMD
  || 'docker exec ffoa-dev-postgres psql -U ffws_dev -d ffws_dev -t -A -c';
```

CI 上没有 `ffoa-dev-postgres` 容器（这是本地 dev 命名）。CI 工作流没设 DB_CMD，所以脚本调 `docker exec ffoa-dev-postgres ...` 全部失败，stderr 一直在刷 `Error response from daemon: No such container`。

但 query() 函数：
```ts
function query(sql) {
  try { return execSync(...).trim(); }
  catch { return ''; }    // ← 静默吞掉
}
```

**所有错误都被吞了**，每个 check 看到的都是空字符串 → 误判成"数据缺失"。最阴险的是这种"伪检查"看起来一直在跑，没人怀疑底层连接是断的。

### 真实影响

- staging deploy 错以为 UAT 库有数据问题，自动报警
- nightly snapshot check 同样问题（snapshot-loader 没 export DATABASE_URL）
- L1c 实际**从来没真正检查过 UAT/snapshot 数据健康度**——它一直在自言自语

### 修法

**核心反模式**：脚本不能静默吞 query 错误。区分"无数据"和"无连接"。

```ts
// 1. 启动期 connectivity probe
const probe = query("SELECT 1");
if (probe !== '1') {
  console.error(`❌ DB connectivity failed, all checks would be false positives`);
  process.exit(2);
}

// 2. query() 出错 throw 而不是吞
function query(sql) {
  return execSync(...).trim();    // 不 catch，让 check() wrapper 抛
}
```

外层每个 check 自己 try/catch（已有），抛出会被识别为该 check fail，但**不会传染整个脚本**。

### 普适规律

写"check 类"脚本必备的两条铁律：
1. **启动期连通性自检**：依赖外部资源（DB / API / 文件）的脚本，第一件事必须 probe 一下，不通就立即退出。
2. **严格区分"未找到数据"与"未能连接"**：`query() → ''` 应该只表示"DB 真返回空"，不能用同一个值表达"连接失败"。catch + return '' 是反模式的典型。

### 关联

- `testing/scripts/data-quality-check.ts` —— 本次重构
- `.gitea/workflows/deploy-uat.yml` —— 删除被假阳性绊住的 L1c 步骤
- `.gitea/workflows/nightly-snapshot-check.yml` —— 让 snapshot-loader 把 DATABASE_URL 写到 GITHUB_ENV
- `scripts/ops/snapshot-loader.sh` —— 新增 `$GITHUB_ENV >> DATABASE_URL`

下游会暴露的真问题（不在本 PR）：performance.quality.ts 的 SQL 用了 `platform_iam.user_roles`、`kpi_assignment.status` 等已经被 prisma schema 改名的 table/column，需要 module 维护者跟进。
