# 2026-05-14 — Gitea label id 硬编码必翻车，永远按名字解析

## 翻车现场

在 #331 claim 流程里调 Gitea API 替换 issue 的 label：

```bash
# AI 凭印象写出来的（错的）
curl -X PUT .../issues/331/labels -d '{"labels":[1,2,23,29]}'
# 期望: Kind/Feature=1, Priority/Medium=2, Status/PRD 中=23, Owner/Chentao=29
# 实际: id=1 是 Kind/Bug，结果 issue 多了一个 Kind/Bug 标签，掉了 Priority/Medium
```

修正方式：先 GET `/labels` 拿到 `name → id` 映射，再 PUT。

## 根因

Gitea 的 label id 是 **per-repo 自增整数**，跟 label name 没有任何对应关系。同一个 name 在两个仓库 id 不同，新建 label 时 id 也会变。**没有任何理由硬编码**——但 AI（包括 Claude）倾向凭印象拍脑袋写 `[1, 2, ...]`，因为看起来 "1=feature, 2=medium" 像自然顺序。

## 防范

1. **永远不硬编码 label id**。CLI 层（`scripts/ops/gitea`）已经用 `_resolve_label_id(name) → id` 强制按名字查询。
2. 任何调 Gitea label API 的代码段，code review 时一旦看到 numeric label id 字面量就要拦。
3. 写新脚本时不要新建"id 常量字典"——下次重置仓库或换实例就全废。

## 为什么这条值得沉淀

这是 #331 工单存在的核心证据之一——AI 在没工具兜底时反复在这种"系统 id 不稳定但 AI 拍脑袋当稳定用"的坑上撞。CLI 强制 name-based 解析后这个坑被关掉。

## 引用

- 翻车 commit context：#331 claim 流程的 PATCH 操作
- 修正落地：`scripts/ops/gitea` `_resolve_label_id` 函数
- spec：`docs/standards/15-cli-design-spec.md` 第 13 节"CLI 与 library 层的关系"——CLI 层就是要把这种"业务原语"封装好，让调用方不接触底层 id
