# FIX-02 精加工 Ap 语义错误 · 根因与修复报告

> 发现者：防退化评测门禁（`D:\NX_AI\tests\run_eval.py`）
> 定位与修复：验收方（DeepSeek Harness），2026-10-16
> 备份：`D:\NX_AI\backup\rule_engine.py.before_FIX02`、`ai_programming_rules_v2.json.before_FIX02`
> 状态：**已修复并验证；门禁达到 13/13 全绿、已知未修 0、意外退化 0**

## 一、现象（修复前）

| 上下文 | 预设 ap_pct | 修复前 Ap | 应取 |
|---|---|---|---|
| P / 45钢退火 / finishing / φ8 平底 | 100.0% | **4.000 mm** | 0.1~0.3 |
| N / 6061铝 / finishing / φ8 平底 | 100.0% | **6.400 mm** | 0.1~0.3 |
| H / 淬火钢HRC50 / finishing / φ6 平底 | 50.0% | **2.400 mm** | 0.1~0.3 |

铝件精加工切深 6.4mm —— 发给车间就是一把刀报废。

## 二、根因

1. **数据层**：`parameters.json` 里精铣**平底刀**的 `ap_pct` 被填成 100.0 / 80.0 / 50.0。
   而规范（任务书 P1-03）写的是：「精加工：**depth_abs 0.1~0.3mm 不折算**；
   Ae 不折」。也就是说**精加工的轴向切深是绝对深度，本来就不该按刀径百分比算**。
   （对照：精铣**球刀**预设的 ap_pct 是 4.0/3.0 —— φ8 得 0.32mm，是合理的；
   平底刀那几个值是历史遗留的脏数据。）
2. **引擎层**：`rule_engine.py` Step 6 无条件按 `ap = ap_pct/100 × D` 计算，
   之后的 RL-006 只做「≤ 0.5D」的上限 clamp —— 对精加工毫无意义（0.5×φ8 = 4mm）。
   于是 8.0mm 被"截断"到 4.0mm，看起来像做了保护，实际仍是荒谬值。

## 三、修复（数据驱动，不硬编码）

**新增红线 RL-009**（写入 `ai_programming_rules_v2.json`，redlines 由 8 条增至 9 条）：

```json
{
  "id": "RL-009",
  "title": "精加工 Ap 是绝对深度，不是刀径百分比",
  "rule": "operation=finishing 时轴向切深必须落在绝对区间 0.1~0.3mm（不按刀径折算）。预设里的 ap_pct 仅作选值参考，最终必须 clamp 进该区间并记 warning。",
  "priority": "critical",
  "applied_in_engine": true,
  "finishing_depth_abs_mm": {"min": 0.1, "max": 0.3, "default": 0.2}
}
```

**引擎新增 Step 6b**：`operation == "finishing"` 时，把按比例算出的 Ap clamp 进
`RL-009.finishing_depth_abs_mm` 区间；越界记 warning 并在 message 里写明
"预设 ap_pct=xxx%"。区间从 JSON 读，改规则不用改代码。

## 四、验证

修复后门禁输出：

```
防退化评测  通过 13/13   已知未修 0   意外退化 0        [退出码 0]
  [OK] RL-009 精加工Ap绝对深度   finishing Ap=0.300mm（要求 0.10~0.30）
  [OK] RL-002 丝锥Fz=螺距        全部 7 把丝锥：Fz==螺距 且 F==rpm×螺距
  ...（其余 11 条全部 OK）
  特征化基线：10 个上下文，漂移 0 处
```

**特征化基线漂移报告**（修复带来的、有意的变更，已被门禁准确捕获）：

```
漂移 9 处
  - P|45钢退火|finishing|end_mill_flat|8.0    ap: 4.0 -> 0.3
  - N|6061铝|finishing|end_mill_flat|8.0      ap: 6.4 -> 0.3
  - H|淬火钢HRC50|finishing|end_mill_flat|6.0 ap: 2.4 -> 0.3
  ...
```

处置流程（这也是门禁设计的正确用法）：
**先跑一次看漂移 → 人工确认漂移是"有意变更" → `--rebuild-baseline` 重建 → 再跑确认归零。**
为便于发版/CI 严格把关，已给门禁加了 `--strict-drift`：把漂移也当失败处理。

同时按事先约定，**已从 `tests/golden/known_issues.json` 删除 FIX-02 条目**
（它已修复，不能让它继续以"已知未修"的名义把门禁维持在红色）。

## 五、防复发

1. **规范里写"绝对量"的地方，不要落到百分比管线里**：精加工 Ap、攻丝螺距都属于"几何锁死量"，
   任何比例换算都是错的。
2. **clamp 要能挡住荒谬值，而不只是"截断到上限"**：0.5D 这种上限对精加工没有保护意义，
   需要**区间**（下限也要有）。
3. **脏数据要在数据层修复或显式豁免**：精铣平底刀的 ap_pct 仍是 100%，
   现由 RL-009 兜住；后续应把这几个预设值按球刀那样改成合理百分比（约 3~4%），
   或在数据字典里标注"该字段对 finishing 无效"。
4. 遗留待办（已派给 Trae 的 FIX-02 卡）：补单元测试（断言 finishing Ap ∈ [0.1,0.3]）、
   写根因文档、复核本次改动。

## 六、施工方复核与补测（FIX-02 卡收尾，2026-10-16）

**复核结论**：RL-009 在规则 JSON（`data/rules/ai_programming_rules_v2.json` L131-144）
与引擎 Step 6b（`rule_engine.py` L346-360）均在位，数据驱动，未改一行业务代码。

**补测**：新增 `tests/test_finishing_ap.py`，9 个用例 **9/9 PASSED**：

| # | 守住的语义 | 实测 |
|---|---|---|
| 1 | 区间数据驱动：JSON 里 min/max/default = 0.1/0.3/0.2 | OK |
| 2 | P 类 φ8 平底脏预设 ap_pct=100%（原始 Ap=8.0）→ clamp 0.3 + warning 写明预设百分比 | OK |
| 3 | **6 个材料类**（P/M/K/N/S/H）平底 finishing 全部 Ap=0.3 且越界必告警 | H/K/M/N/P/S 全 0.3 |
| 4 | **与刀径无关**：φ2/φ8/φ20/φ100 精加工 Ap 全=0.3（不按直径折算的铁证） | OK |
| 5 | 合理预设不冤枉：球刀 3%×φ6=0.18 在区间内 → 不动值、不告警 | OK |
| 6 | 下限生效：球刀 3%×φ2=0.06 → clamp 0.1 + 告警 | OK |
| 7 | 上限小越界也夹：球刀 4%×φ8=0.32 → 0.3 + 告警 | OK |
| 8 | roughing 不受 RL-009 影响：φ12 粗加工仍走比例/RL-006 管线（4.8mm），无 RL-009 告警 | OK |
| 9 | RL-009 只管 Ap 不管 Ae：ae_pct=8%×φ8=0.64mm 保持不变 | OK |

验收命令：`python D:\NX_AI\tests\test_finishing_ap.py`