发现于 #5559 (os-dev 收尾清单补 skip-changeset 硬步骤)的前提核对:逐 PR 拉 timeline 的 labeled/unlabeled 事件时,发现标签写入双向互抹。查重:仓内无既有单(搜 label clobber / labeler / sync-labels、skip-changeset label 均无命中;#5620 是 allow-major 从事件载荷读标签的孪生竞态,与本条不同 —— 那是读 ,这是写 )。
事实(六例 timeline,2026-08-05)
方向一:agent 的写入抹掉 bot 的标签。 每次 agent 挂 skip-changeset,同一秒都伴随一条 agent 自己发出的 unlabeled ,抹掉 bot 刚挂的标签,bot 随后再补挂:
这只有一种解释:挂标签走的是整集替换 (PUT /issues/{n}/labels,或 MCP 的 labels 字段),而不是新增 (POST /issues/{n}/labels)。整集写入把当时读到的标签集覆盖回去,于是并发 bot 刚挂的标签被抹除。
方向二(有实际代价):bot 的写入抹掉 agent 的 skip-changeset。 #5533 :
15:46:42 labeled size/m by github-actions[bot]
15:46:44 unlabeled size/m by os-zhuang # agent 整集写入,抹掉 size/m
15:46:44 labeled skip-changeset by os-zhuang # 豁免标签落地
15:46:45 unlabeled skip-changeset by github-actions[bot] # 一秒后被 CI 抹掉
15:46:45 labeled size/m by github-actions[bot]
15:46:45 labeled tests by github-actions[bot]
15:47:08 labeled skip-changeset by os-zhuang # 只好再挂一次
skip-changeset 是 Check Changeset 的门禁豁免依据 。它被 CI 抹掉一秒,直接制造了 #5559 立单时记录的现象「dev 声明的标签没落到 PR 上」—— 实际是落了又被抹,dev 侧看起来与「没挂」无法区分。
为什么值得修
两个半独立的落点
写入侧(客户端) :挂单个标签必须走新增语义(POST .../issues/{n}/labels,body {"labels":["skip-changeset"]}),不得整集替换。若 MCP 的 issue label 写入本身是 PUT 语义,则约束落在 agent 指令面(finding(os-dev): 测试-only PR 的 skip-changeset 标签两次由 dev 声明却未落到 PR 上 —— 疑似 os-dev 定义/工具面缺口,每次都靠 PM 兜底 + 烧一轮门禁红 #5559 的新条款已写明「只加一个标签,不要整集写入」)。
CI 侧 :确认 actions/labeler@v7.0.0(.github/workflows/pr-automation.yml 的 Auto Label)与 codelytv/pr-size-labeler@v1.10.4(Check PR Size)各自是否在做整集同步 —— 方向二说明至少有一个会移除自己没管的标签。若是 labeler 的 sync-labels 语义,应显式关掉;若是 size-labeler 的实现,需要评估其对无关标签的处理。
⚠️ pr-automation.yml 今日已被 Check Changeset 从事件载荷读 skip-changeset 标签:开 PR 后 5 秒内加标签,首个 run 永久红(重跑复用载荷)—— 一日三例 #5580 /fix(ci): Check Changeset 实时读 skip-changeset 标签,首个 run 不再永久红 (#5580) #5625 改过,pr-automation.yml 的 allow-major 步骤仍从事件载荷读标签:#5580 同款竞态的孪生体(pre-mode 期间休眠) #5620 也在排队动同一文件,改动请先协调避免撞车。
复现
curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/objectstack-ai/objectstack/issues/5533/timeline?per_page=100" \
| node -e '...filter(e => ["labeled","unlabeled"].includes(e.event))'
按 created_at 排序打印 event | label | actor,上文序列即原样可见。
Found-during: #5559 (devx 车道第 7 轮,dev 会话 session_01GX3sL71LFq8m2usg6VqTSE)的前提核对。unassigned,交 PM 分诊。
发现于 #5559(os-dev 收尾清单补
skip-changeset硬步骤)的前提核对:逐 PR 拉 timeline 的labeled/unlabeled事件时,发现标签写入双向互抹。查重:仓内无既有单(搜label clobber / labeler / sync-labels、skip-changeset label均无命中;#5620 是 allow-major 从事件载荷读标签的孪生竞态,与本条不同 —— 那是读,这是写)。事实(六例 timeline,2026-08-05)
方向一:agent 的写入抹掉 bot 的标签。 每次 agent 挂
skip-changeset,同一秒都伴随一条 agent 自己发出的unlabeled,抹掉 bot 刚挂的标签,bot 随后再补挂:21:32:02bot 挂size/s→21:32:06agentunlabeled size/s+ 挂skip-changeset→21:32:17bot 重新挂size/s21:38:42bot 挂documentation→21:38:47agentunlabeled documentation+ 挂skip-changeset→21:39:00bot 重新挂16:43:23agent 一次抹掉documentation+size/l+tests,bot 于16:43:35/16:43:37全部补挂这只有一种解释:挂标签走的是整集替换(
PUT /issues/{n}/labels,或 MCP 的 labels 字段),而不是新增(POST /issues/{n}/labels)。整集写入把当时读到的标签集覆盖回去,于是并发 bot 刚挂的标签被抹除。方向二(有实际代价):bot 的写入抹掉 agent 的
skip-changeset。 #5533:skip-changeset是 Check Changeset 的门禁豁免依据。它被 CI 抹掉一秒,直接制造了 #5559 立单时记录的现象「dev 声明的标签没落到 PR 上」—— 实际是落了又被抹,dev 侧看起来与「没挂」无法区分。为什么值得修
.claude-only PR 在本仓是常态形状。size/*、documentation、tests的抹除-补挂抖动是每 PR 都在发生的噪声(timeline 里每次两三条冗余事件)。skip-changeset标签两次由 dev 声明却未落到 PR 上 —— 疑似 os-dev 定义/工具面缺口,每次都靠 PM 兜底 + 烧一轮门禁红 #5559 立单与派发词都是这么归因的),排查成本重复付。两个半独立的落点
POST .../issues/{n}/labels,body{"labels":["skip-changeset"]}),不得整集替换。若 MCP 的 issue label 写入本身是 PUT 语义,则约束落在 agent 指令面(finding(os-dev): 测试-only PR 的skip-changeset标签两次由 dev 声明却未落到 PR 上 —— 疑似 os-dev 定义/工具面缺口,每次都靠 PM 兜底 + 烧一轮门禁红 #5559 的新条款已写明「只加一个标签,不要整集写入」)。actions/labeler@v7.0.0(.github/workflows/pr-automation.yml的 Auto Label)与codelytv/pr-size-labeler@v1.10.4(Check PR Size)各自是否在做整集同步 —— 方向二说明至少有一个会移除自己没管的标签。若是 labeler 的sync-labels语义,应显式关掉;若是 size-labeler 的实现,需要评估其对无关标签的处理。skip-changeset标签:开 PR 后 5 秒内加标签,首个 run 永久红(重跑复用载荷)—— 一日三例 #5580/fix(ci): Check Changeset 实时读 skip-changeset 标签,首个 run 不再永久红 (#5580) #5625 改过,pr-automation.yml 的 allow-major 步骤仍从事件载荷读标签:#5580 同款竞态的孪生体(pre-mode 期间休眠) #5620 也在排队动同一文件,改动请先协调避免撞车。复现
按
created_at排序打印event | label | actor,上文序列即原样可见。Found-during: #5559(devx 车道第 7 轮,dev 会话
session_01GX3sL71LFq8m2usg6VqTSE)的前提核对。unassigned,交 PM 分诊。