## [ERR-20260509-006] `git add` 后再 Edit 文件，新改动**不会**自动进 staged → commit 漏掉

**日期**: 2026-05-09
**类别**: 工作流 / git
**严重度**: 中（运行时不 fail，但 PR description / learning 与实际代码不一致；信任度问题）

### 现象

PR #269（PR-B CI flock 互斥锁修复）流程里：
1. 改了 `quality-gates.yml` + `deploy-uat.yml`
2. `git add` 把 yaml stage 进 index
3. 跑 `/simplify` review，3 个 agent 提出 4 处 finding
4. 做 4 次 `Edit` 修复 finding（lsof 诊断 ×2、Phase 注释清理 ×N、run-backend-integration.sh 警示）
5. 跑 `git add .learnings/2026-05-09-ci-flock-shared-test-containers.md`（**只 add learning**）
6. 跑 `git diff --cached --stat` → 显示 4 文件 +178/-59
7. commit + push + CI 全过 + auto-merge

merge 后 grep PR-B commit 真实 diff，发现 `lsof` / `Phase` / `docker ps -a` 字符串**完全不存在**——意味着 simplify 4 处 Edit 中的 3 处（`quality-gates.yml` lsof 诊断 + Phase 注释；`deploy-uat.yml` lsof 诊断）**没进 commit**，只有 `run-backend-integration.sh` 警示进了。

### 根因

**`git add` 是 snapshot at that moment**，把 working tree 当时的版本拷一份到 index。
此后再 `Edit` 该文件，working tree 变了但 **index 不变**——必须重新 `git add` 才会同步。

具体到本次：
- step 2: `git add quality-gates.yml deploy-uat.yml` → index = simplify **前**版本
- step 4: `Edit` 4 处 → working tree = simplify **后**版本，index 不变
- step 5: 只 `git add learning`（顺手 `git add run-backend-integration.sh` 在另一处 Bash 调用里发生，因此那个文件 index 跟着更新了）
- step 6: `git diff --cached --stat` 看的是 index 的 +178/-59，stat 数字"看着对"——**因为 simplify Edit 之前 stage 的初版改动量本来就接近这个数**。stat 不能验内容。
- step 7: commit 出去的是 simplify **前**版本

为什么会以为 stat 验证够？因为我之前 PR-A 做完 simplify 4 处 Edit 之后**真的有** re-add。但 PR-A 是直接 git add 整个文件清单，PR-B 我只 add 了 learning（assumption 是"yaml 我前面 add 过了"）——这个 assumption 是错的。

### 影响

- ✅ 功能：PR-B 互斥锁逻辑正确（这部分是 simplify **前**的 Edit，进了 commit）
- ❌ 诊断：取锁失败时缺 `lsof` + `docker ps` 输出，oncall 仍要 ssh 上 runner 查
- ❌ 可读：`# ===== 原 "Reset test containers" =====` 这种 simplify 之前的"原 step 名"标记没改成 `# --- Phase X ---`
- ❌ 信任：PR description / learning 已声称有这些改动，跟实际代码不一致

补遗在 PR-B-2 (#TBD) 一并修。

### 预防措施（按优先级）

#### 1. **每个 `Edit` 后立刻 `git add` 该文件** ★（推荐）

让 staged = working tree 始终一致。Edit 完一处，跟 add 一行，无需记忆。

#### 2. **`git commit` 之前必跑 `git status` 看 unstaged**

```bash
git status -s
# 期望：M  <staged-files> 全部带 `M ` 前缀（M 在第一列 = staged，第二列空）
# 如果第二列也有 M → 该文件有 unstaged 改动 → 必须 re-add
```

`git diff --cached --stat` 不够——它只看 index，不告诉你 working tree 是否更新过。

#### 3. **小心 `git diff --cached --stat` 的"看着对"陷阱**

stat 行数 ≈ 改动量级，但**不能验内容**。可以用 `git diff --cached <file> | grep <预期改动关键字>` 快速 sanity check。例如这次本应：
```bash
git diff --cached .gitea/workflows/quality-gates.yml | grep -E 'lsof|Phase' && echo OK
```

#### 4. （慎用）`git commit -a` 自动 add 已跟踪文件改动

跳过手动 add 步骤，但**不包含未跟踪文件**（新文件还得显式 add）。混合使用易错——本次 learning 是新文件，`-a` 不覆盖。

#### 5. （仪式化）pre-commit hook 加"未 staged 改动警告"

可选 enhancement：hook 检测 `git diff --quiet` 返回非 0（有 unstaged 改动）时弹警告"是否漏 add？"。**但**它会跟"故意 commit 部分"冲突，需要 opt-out 机制。**ROI 低，不做**。

### 关联

- PR-B（#269）实际合并的 commit `169d3002` —— 缺失 simplify 改动
- PR-B-2 修补
- 同样的 `git add` 后再编辑：常见 git 反模式，见 Pro Git 第 2 章。

### Metadata

- Reproducible: yes（任何人 `git add` 后再 `Edit` 都会触发）
- Related: 工作流 / AI 协作（Edit + Bash 工具组合时容易漏 re-add）
