# 5 条元根因规则

> 5 条**适用于所有未来工作**的硬性约束，对应 [`11-troubleshooting-methodology.md`](./11-troubleshooting-methodology.md) 里识别出的 5 类元根因。本文档是这 5 条规则的**单一真相源**——所有 docs / skills / CI 提到"5 条规则"必须链回这里。

## 背景

[工单 #242](http://43.130.59.228/FFAIWorkspace/workspace/issues/242) 复盘识别出 5 类元根因（治理层缺失），单凭 learning + 记忆约束生效是"靠人不靠系统"。本文档把这 5 类提升为**项目级硬约束**，每条规则有明确的：

- 适用范围（什么改动会触发它）
- 规则原文（要满足什么）
- docs 落地 checklist（已经在哪里写明 / 待补）
- 工程化保险依赖（引到 [工单 #249](http://43.130.59.228/FFAIWorkspace/workspace/issues/249) 的子项编号）

**docs 部分（本文档）和工程化保险部分（#249）必须同时具备**，缺工程化保险等于规则未真正落地。

---

## 规则 1：契约面必须有"唯一真相源 + 派生位多点钉死 + 自动校验"

### 适用范围

env 变量名、版本号（Node / PG / Redis）、API 字段、数据库字段、状态枚举、权限点、UI 关键交互。

### 规则

任何契约面只允许 1 个真相源，所有派生位（代码读、example、deploy 脚本、文档）必须显式同名引用，且必须有自动化校验拦"漂移"。

### docs 落地

- [x] 契约面识别流程（API/Schema/状态机/权限/UI/事件）—— [`.agents/skills/contract-check/SKILL.md`](../../.agents/skills/contract-check/SKILL.md) 已有完整定义
- [x] env 变量管理 —— `CLAUDE.md` 「环境变量」段已规定"新增 `process.env.X` 必须同步加 `.env.example`"
- [x] 字段对齐校验 —— 测试金字塔 L0a/L0b/L0c（[`05-development-workflow.md`](./05-development-workflow.md)「测试策略」段）
- [x] **PR 模板加契约面 checkbox** → 已落地 [`.gitea/PULL_REQUEST_TEMPLATE.md`](../../.gitea/PULL_REQUEST_TEMPLATE.md)「契约面检查」段（工单 #249 子项【8】）

### 工程化保险依赖

- ✅ 工单 #249【9】反向 env 覆盖检测 —— [`scripts/ops/check-env-coverage.sh`](../../scripts/ops/check-env-coverage.sh) 扩三层：① 正向（代码 → example）② zombie（example → 代码）③ `--check-deploy <uat\|pro> [path]`（example 必填集 → 实际机器 .env.{uat,pro} 的 key 集，按注释 tag 算期望）。Tag 语法支持 `# [prod-only]` / `# [dev-only]` / `# [optional]` / `# [可选]`。
- ✅ 工单 #249【8】PR 模板 checkbox

---

## 规则 2：依赖关系演化必须可见

### 适用范围

`package-lock.json` / `requirements.txt` / `Cargo.lock` 等 lock 文件变更，含 transitive dep。

### 规则

任何 lock 文件主版本变化必须显式 review；deploy 必须强制 reload 进程，不允许"装完不重启"的尾巴；已知 ESM 化风险包必须 pin。

### docs 落地

- [x] 依赖升级提示 —— `CLAUDE.md` 「Git 规则摘要 → PR 拆分准则」段把 dep 升级隐含在"高风险路径"
- [ ] **`docs/standards/` 加章节："依赖升级 PR review 注意事项"** —— 待 #249【2】lockfile workflow 落地后一并补
- [ ] 部署强制 reload —— 见工程化保险

### 工程化保险依赖

- ✅ 工单 #249【2】+【10】lockfile 主版本变化标记 + 高风险包 watchlist —— 合并实现：
  - [`.gitea/workflows/lockfile-diff.yml`](../../.gitea/workflows/lockfile-diff.yml) 在 PR 时 diff 改动的 `package-lock.json`（base vs head），列出 transitive dep major bump
  - [`scripts/ops/lockfile-diff.py`](../../scripts/ops/lockfile-diff.py)（Python）解析 lockfile + glob 匹配 watchlist
  - [`scripts/ops/lockfile-risk-watchlist.txt`](../../scripts/ops/lockfile-risk-watchlist.txt) 高风险包名单（@scure/* / @noble/* / otplib / uuid / jose 等，基于 #242 教训）
  - 命中 watchlist → PR comment ⚠️ 高亮（不阻断，软规则；跟 PR 模板「依赖」checkbox 双层防御）
- ✅ 工单 #249【4】Deploy 强制 `pm2 reload --update-env` —— 已在 [`scripts/deploy/deploy.sh`](../../scripts/deploy/deploy.sh)（搜 `pm2 reload --update-env`）落地，`deploy-production.yml` 注释明示 `git pull → build → pm2 reload` 链路

---

## 规则 3：环境一致性必须工具化

### 适用范围

本地 / CI / UAT / PROD 4 套环境 × 多个产品。

### 规则

环境差异必须由工具检测，不靠人记忆；装机必须脚本化，禁止 bash_history 决策；部署前必须做版本 fail-fast。

### docs 落地

- [x] 环境拓扑 —— [`docs/ops/01-server-infrastructure.md`](../ops/01-server-infrastructure.md)
- [x] env 文件架构 —— [`docs/ops/`](../ops/) 下相关文档
- [ ] **`docs/ops/01-server-infrastructure.md` 写实际版本号（不写"20.x"模糊范围）** —— 待 #249【3】env-drift 脚本能自动对账后由脚本生成
- [ ] 装机决策禁止"# 22 或者 20" 含糊命令注释 —— 待 #249【6】provision-server.sh 取代手工命令

### 工程化保险依赖

- ❎ 工单 #249【3】跨环境对账脚本 `check-env-drift.sh` —— **已弃**：env 验证由 CI（[`quality-gates.yml:181`](../../.gitea/workflows/quality-gates.yml) 跑 audit）+ 部署前（[`deploy-production.yml:39`](../../.gitea/workflows/deploy-production.yml) 跑 `env-check.sh`）双层覆盖；跨机器 ssh drift 是重资产（4 台 SSH key + cron + 通知），价值与维护成本不匹配
- ✅ 工单 #249【6】装机一键脚本 —— [`scripts/deploy/setup-production.sh`](../../scripts/deploy/setup-production.sh)（465 行）已覆盖：Docker / Node / PM2 / Nginx / Certbot 安装 + 系统配置 + 目录结构 + 配置模板生成。命名不叫 `provision-server.sh` 但功能等价
- ✅ 工单 #249【7】部署前 Node 版本 fail-fast —— [`scripts/deploy/deploy.sh`](../../scripts/deploy/deploy.sh) `verify_node_version()` 在脚本顶部跑：读 `backend/package.json` engines.node 期望（动态，不硬编码） vs `node -v` major，不满足 exit 1 + nvm install 引导。3 个 deploy workflow（uat/test/production）通过 ssh 调 deploy.sh 自动受益，无需改 workflow（规则 1：单一真相源避免派生位漂移）

---

## 规则 4：完工定义 = 实测业务行为

### 适用范围

所有"装好了 / 配好了 / 改好了"的运维操作、所有部署 / 配置变更 / 修复 PR 的"完工"声明。

### 规则

**完工 ≠ 命令成功 ≠ 文件存在 ≠ grep 命中** —— 必须实测对应的**业务 API 响应正确**才算完工。多环境批量任务必须用"环境 × 状态"显式表格，不靠记忆。

### docs 落地

- [x] **`.agents/skills/deploy-ops/SKILL.md` 加约束**（本 PR 落地）
- [x] 测试金字塔 L1/L2/L3 都强调"实测业务行为"—— [`05-development-workflow.md`](./05-development-workflow.md)「测试策略」段
- [x] P0/P1 复盘模板"应用层修复"段要求"4 环境验证结果" —— [`11-troubleshooting-methodology.md`](./11-troubleshooting-methodology.md) 复盘模板
- [x] PR 模板加段："实测验证步骤 + 实测结果" —— 已合并到 [`.gitea/PULL_REQUEST_TEMPLATE.md`](../../.gitea/PULL_REQUEST_TEMPLATE.md) Test plan 段末尾（部署/配置/迁移类必填实测项）

### 工程化保险依赖

- 工单 #249【5】Healthcheck 加严（从 TCP 端口活着 → 业务可用）—— 跟规则 5 共享

---

## 规则 5：监控告警必须验业务

### 适用范围

所有 healthcheck 和告警阈值，含 PM2 进程、Docker 容器、应用 API、外部依赖。

### 规则

healthcheck 验**业务可用**（DB 连得上 + Redis 连得上 + Temporal 能 list namespace），不是"TCP 端口活着"。任何异常必须能立刻推到 webhook（钉钉/飞书），不依赖人查日志。

### docs 落地

- [x] 故障运行时诊断 —— [`.agents/skills/troubleshoot/SKILL.md`](../../.agents/skills/troubleshoot/SKILL.md)
- [x] 告警阈值与 webhook 配置文档 —— [`docs/ops/08-pm2-restart-watch.md`](../ops/08-pm2-restart-watch.md)（PM2 ↺ 部分）；【5】Healthcheck 加严文档待补

### 工程化保险依赖

- ✅ 脚本就绪 / ⏳ 部署 + 演练：工单 #249【1】PM2 ↺ 阈值告警
  - 脚本：[`scripts/ops/pm2-restart-watch.sh`](../../scripts/ops/pm2-restart-watch.sh)
  - 部署：[`docs/ops/08-pm2-restart-watch.md`](../ops/08-pm2-restart-watch.md)（4 台业务主机 cron + webhook + 主动制造告警演练，待 deploy-ops 执行）
- ✅ 部分 / ⏳ Backend HTTP：工单 #249【5】Healthcheck 加严
  - **A. Docker 容器 healthcheck** ✅：Temporal 容器加 `temporal operator namespace list` 业务可用检查（[`docker/docker-compose.yml`](../../docker/docker-compose.yml)）
  - **B. PM2 健康参数** ✅：backend / frontend 加 `min_uptime` `listen_timeout` `max_restarts` `exp_backoff_restart_delay` 4 个；temporal-worker 段已有 `min_uptime` + `listen_timeout` + `kill_timeout` 历史配置（[`scripts/deploy/deploy.sh`](../../scripts/deploy/deploy.sh) PM2 config 生成段）
  - **C. Backend HTTP `/api/v1/health/detailed` 加 Redis + Temporal 检查** ⏳：[工单 #295](http://43.130.59.228/FFAIWorkspace/workspace/issues/295) 跟踪（涉及后端代码 + L1 集成测试，跨本 PR 主题）

---

## 验收 / 演练

5 条规则要满足下面任一条件才算"真正生效"：

- [ ] 每条规则各自至少 1 个工程化保险落地（CI / hook / 告警 / 脚本）
- [ ] **至少 1 个规则做"主动制造违规 → 看是否被拦"演练**（推荐先选规则 1：故意把 `JWT_ACCESS_TTL` 从代码里删掉，看 CI 是否报"配了不读"）
- [ ] CLAUDE.md / AGENTS.md 第 10 条引用本文档（已落地）
- [ ] 本文档每次有规则跨过"docs 落地完成 + 工程化保险落地完成"门槛时更新对应 checkbox

---

## 关联

- 工单 #251（5 条规则融入工作流，本文档是其 docs 部分的实现）
- 工单 #249（5 条规则的工程化保险，21 个落地项里 9 个直接引用 #249 子项）
- 工单 #250 → [`11-troubleshooting-methodology.md`](./11-troubleshooting-methodology.md)（方法论，本文档的元规则来源）
- 工单 #242（5 元根因的诞生案例）
