From 88db7d6700acfc96b1cfb14d2d537457d430d875 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 21:43:58 +0000 Subject: [PATCH] =?UTF-8?q?docs(pm-dispatch):=20step=200=20=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E5=88=A4=E6=8D=AE=E6=89=A9=E4=B8=BA=E4=B8=89=E4=B8=AA?= =?UTF-8?q?=E6=9E=90=E5=8F=96,=E5=85=9C=E4=BD=8F=E4=B8=A4=E7=A7=8D?= =?UTF-8?q?=E4=B8=8D=E5=8F=AF=E8=A1=8C=E5=8A=A8=E7=9A=84=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E5=BD=A2=E7=8A=B6=20(#6405)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧判据只选「无 `pm:*` 标签」的 issue,两种卡片形状落在判据之外却仍不可行动,今日 各实测到停滞: - 盲区 A(实测 6 张,11:00–13:55Z 立、16:47Z 才靠两个 PM 会话手工报告捞起; #5794 / #5744 各停滞整日 ⇒ 第三、第四次同形):有 `pm:queue`、无 `domain:*`。 跨座位转移卡与队列管家队列健康卡按协议**天然预先带 `pm:queue`**,又按单一 生产者规则不准自打 `domain:*` ⇒ 看板显示可派、实际谁都不能认领。生产者是 协议本身,不是某个车道的坏习惯。 - 盲区 B(实测 1 张,停滞 ~69 分钟):有 `domain:*`、无 pm-state。同一条纪律 当天早上刚写在另一张卡上,几小时后 PM 立单时照犯 ⇒ 纪律写在别处不够, 判据本身必须兜住。 判据改为三个析取:① 无 `pm:*` 且无 `domain:*`(今天的规则,不变;`domain:*` 一项此前只活在实践里,析取 ③ 出现后必须写明);② 有 `pm:queue` 但无 `domain:*`, **仅限 objectstack**;③ 有 `domain:*` 但无 pm-state。排除项不变。 两条护栏: - 析取 ② **repo-scoped**。兄弟仓是整仓座位,车道标签在那里根本不存在(实测 objectui 查无 `domain:devx` 标签),「有 `pm:queue` 无 `domain:*`」是它们每一张 队列卡的正常形状 —— 不限定即一次扫进 objectui 38 + cloud 19 张。 - 析取 ②③ 的年龄下限键在 **`updated_at` 而非 `created_at`**:半标注状态既可能 来自刚立的卡,也可能来自一次标签写入把老卡打成半标注(分诊自己打标签就是 分开的两次写),`created_at` 漏掉后者。 发布件 `skills/objectstack-pm-dispatch/SKILL.md` 有同构的 step 0 面(L173-188 同样 以「carrying no `pm:*` / `needs-user-decision`」为判据),按 #5451 route B 加泛化版: 写成机制描述,不带本仓 issue 号,不引入该文件不存在的标签词汇(`domain:*` / `finding` / `pm:on-hold` 在发布件里均无定义),`role` 词计数仍为 0。 三轴决策框架未触碰:`check:skill-frame-sync` 改前改后输出 **byte-for-byte 相同** (md5 6aa2a81538f6ffa6e0d642a216aa9133,cmp 无差异)。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3 --- .claude/skills/pm-dispatch/SKILL.md | 47 ++++++++++++++++++++++- skills/objectstack-pm-dispatch/SKILL.md | 50 ++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index f8ed78f945..6f2ea6d630 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -790,8 +790,48 @@ in-scope,验收依据 …」),维护者可否决。 (**分诊座位的活** —— 执行座位跳过本步,rule 4。) The maintainer does not pre-sort the backlog. On every round (and every -idle check-in), sweep issues that carry no `pm:*` / `needs-user-decision` -label and classify each: +idle check-in), sweep every issue matching **任一**析取,并逐张分类: + +1. **无 `pm:*`、无 `needs-user-decision`、也无 `domain:*`** —— 全裸卡。今天的 + 规则,判据不变;`domain:*` 一项此前只活在实践里(带车道标签的单被读作 + 「已路由」而跳过),析取 3 出现后必须写明,否则两条互相吞掉。 +2. **有 `pm:queue` 但无 `domain:*`** —— ⚠️ **仅限 `objectstack-ai/objectstack`**。 +3. **有 `domain:*` 但无 pm-state**(`pm:queue` / `pm:dispatched` / `pm:blocked` / + `pm:on-hold` / `finding` / `needs-user-decision`)。 + +⏳ **析取 2、3 只取 `updated_at` 早于 ~2 分钟前的卡**(析取 1 无此限)。 + +**为什么 2、3 不是可选项。** `domain:*` 是**路由**、pm-state 是**状态机**,只带 +其一的单对两个视图**同时**不可见 —— 队列按 pm-state 取,车道认领按 `domain:*` +取,而旧判据「带了 `pm:*` 就跳过」把补票的最后一道也关上了。这不是假想:同形 +已四次实测(两次各停滞整日,一次同日六张 11:00–13:55Z 立、16:47Z 才被两个 PM +会话**手工**报回来才捞起)。 + +- 析取 2 的生产者是**协议本身**,不是某个车道的坏习惯:跨座位转移卡(转出方 + 按转移协议自带 `pm:queue`)与队列管家 Routine 的队列健康卡,**按设计**预先 + 带队列标签,又**按单一生产者规则不准自打 `domain:*`**(管家原话:「no + `domain:*` / `repo:*` label applied — routing labels are the triage seat's + single-producer territory. Only `pm:queue` is set here.」)。于是它们在看板上 + 显示可派、实际**谁都不能认领**。 +- 析取 3 那张实测卡是**分诊自己写的纪律的反例**:同一条「立单方别自打 + `domain:*`、留给分诊」当天早上刚写在另一张卡上,几小时后 PM 立单时照犯, + 卡片对队列与扫描同时隐身 ~69 分钟。⇒ **纪律写在别处不够,判据本身必须兜 + 住** —— 这是本节存在的理由,不是修辞。 + +⚠️ **析取 2 必须 repo-scoped,否则它自己就是噪声源。** 兄弟仓(objectui / +cloud)是**整仓座位**,车道标签在那里**根本不存在**(实测 2026-08-07:objectui +的 `domain:devx` 查无此标签),所以「有 `pm:queue`、无 `domain:*`」是它们**每一 +张**队列卡的正常形状 —— 不限定就一次扫进 objectui 38 + cloud 19 张(同日实测 +open 计数),把分诊轮淹掉。 + +⏳ **年龄下限键在 `updated_at`,不是 `created_at`。** 部分标注状态有两个来源: +刚立还没打完标签的新卡,以及**一次标签写入**把老卡打成半标注 —— 分诊自己打 +标签就是分开的两次写(`domain:*` 与 `pm:queue` 各一次),中间那几秒正好落在 +析取 2 / 3 里。按 `created_at` 判会漏掉后一种,按 `updated_at` 两种都兜住;代价 +是一条评论也会把卡推迟一轮,可接受(下一轮即取到)。 + +**分类动作**(对上面选中的每一张;析取 2 选中的卡只欠 `domain:*`,补它即可, +⛔ 不重打已在位的 `pm:queue`): - **Auto-queue (`pm:queue`)**: a concrete defect with a named location or repro; a scoped tooling/gate fix; a restore-invariant finding; a @@ -816,6 +856,9 @@ label and classify each: `tracking` 与 `status:parked` 的 issue(它们的状态由别的机制管,分诊不重判)、 以及 **#4604 与全部 `pm:seat` 座位贴**(协议载体,不是待分诊的工作)。存量大时**每轮 限量、优先最新**(试点用 ~15 条/轮),防一轮吃光存量把轮次拖过一个调度周期。 +⚠️ 排除项在析取 3 下更吃重:parked 单**正常形状**就是「带 `domain:*`、无 +pm-state」(2026-08-07 实测:3 张 `status:parked` 全部长这样),漏判排除就是每轮 +把它们重新扫回来一次。 #### 发现分诊轮 —— 队列的出水口(objectstack#4949) diff --git a/skills/objectstack-pm-dispatch/SKILL.md b/skills/objectstack-pm-dispatch/SKILL.md index 8b5c79f4ab..571abb29f9 100644 --- a/skills/objectstack-pm-dispatch/SKILL.md +++ b/skills/objectstack-pm-dispatch/SKILL.md @@ -173,9 +173,57 @@ invariant is what makes the loop resumable and the board honest. ### 0. Backlog sweep — classification is a standing duty, not a request The maintainer does not pre-sort the backlog. On every round (and every idle -check-in), sweep issues carrying no `pm:*` / `needs-user-decision` label and +check-in), sweep every issue matching **any one** of these three disjuncts, and classify each: +1. **No `pm:*` / `needs-user-decision` label, and no classification label** + either — none of the labels your project reads as "already routed" (area, + component, owning team, lane). The plain rule. +2. **`pm:queue` present, classification label absent** — scoped to the + repositories that actually use classification labels (see the caution). +3. **A classification label present, but no queue state** (`pm:queue`, + `pm:dispatched`, any other `pm:*` state your setup defines, or + `needs-user-decision`). + +Disjuncts 2 and 3 take only cards whose `updated_at` is more than a minute or +two old; disjunct 1 needs no such floor. + +**Why the sweep is a disjunction and not one "unlabeled" filter.** Routing and +queue state are two independent axes, and a card carrying exactly one of them +is invisible on **both** views at once: the queue lists by queue state, while +claimants filter by classification — and a predicate that skips anything +already carrying a `pm:*` label shuts the last door. Neither half is one +filer's bad habit; both have standing producers: + +- A card can arrive **pre-queued by the protocol itself**. Cross-seat transfer + tickets (the handing-off side applies the queue label as part of the + transfer) and tickets filed mechanically by a steward automation both carry + `pm:queue` on arrival, while a single-producer discipline for classification + labels forbids those same producers from applying one. The card then reads as + dispatchable on the board and is claimable by nobody. +- The mirror shape — classified but stateless — comes from anyone who files + with an area label out of habit. Writing the discipline down elsewhere does + not fix it: a project that had published exactly that rule hours earlier + still produced such a card the same day. **The predicate itself has to + absorb it.** + +⚠️ **Any disjunct keyed on a label's absence must be scoped to the +repositories where that label exists.** Where it does not exist, its absence is +universal, so the disjunct matches every open queue card in that repository and +the sweep becomes its own noise source — the check is whether the label exists +there at all, not whether some card happens to carry it. Whatever your project +already excludes from the sweep (parked work, tracking issues, protocol posts) +stays excluded, and disjunct 3 makes that exclusion list load-bearing: a parked +card's normal shape is precisely "classified, no queue state". + +⏳ **Give the partial-labeling disjuncts an age floor keyed on `updated_at`, +not `created_at`.** A half-labeled card has two sources: one just filed, and an +older one that a **label write** has just put into that state — and since +labels are applied one write at a time, the sweep's own labeling passes through +the half-labeled shape for a few seconds. `created_at` misses the second +source; `updated_at` covers both, at the cost of a freshly commented card +waiting one more round. + - **Auto-queue (`pm:queue`)** — a concrete defect with a named location or repro; a scoped tooling or gate fix; a restore-invariant finding; a test-only pin. There is nothing to ask: label it and it becomes dispatchable.