diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index 0528ba3425..7bd06c76ad 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -1818,10 +1818,41 @@ dev 用五项检查验证了它,其中一条是**反向证据**:`spec/src/api/an 第 3 条最容易被省掉,而省掉它就退化成最坏形态:前提不成立时 dev 自行改选,那正是**无人 裁决**的状态,且没有任何读数会显示它发生过。 +**两条元判据 —— 一整族近似单默认不进决策箱(维护者 2026-08-07 决策箱第 2 轮拍板)。** +上面的「不升级四类」说的是**单张单**自带裁决;这两条说的是**一族形状相同的单**不必逐张 +问 —— 族里第一张已经裁过,后来的默认继承那条裁决。重复立卡不是谨慎,是拿维护者的时间 +买同一个答案。 + +- **静默丢弃的声明,默认并入既有拒收集。** 适用判据:一个**已声明的键**在组件的某一支上 + 被**静默忽略**,而更早的裁决已把同一个键在**兄弟支**上定成**响亮的编写期错误**。此时 + 新支**默认并入既有拒收集** —— 复用母单的裁决直接入队派发,⛔ 不为它另开决策箱槽位; + 只有两支之间存在**真实语义差异**时才重开。出处:#5714 把 `pool`-on-sqlite 裁成编写期 + 错误之后,#5931(`memory` 支)仍占了一个槽位,而它只是那条裁决的一词之差的外延;同族 + 重复整周都在发生。⚠️ **边界必须与本条同段读,否则这条捷径会被用过头**:继承的是**裁决 + 连同它的理由**,不是「拒收」两个字 —— 母单的理由**被实测为分支特有**时本条不适用。 + #5739(维度侧)的理由是「拒收会连带拒掉今天已经能跑的查询」,该理由在**度量面上被 + 证伪**,所以 #5918 另裁一次是对的。判法固定:把母单的理由拿到新支上复核一遍,理由不 + 成立就回正常升级路径,别让「同一个键」这个表面相似度替你做判断。 +- **一个操作两个实现,默认治理侧胜出。** 适用判据:同一个操作存在两处实现,且两处行为 + 不一致。**带治理的那一侧**(权限闸、用户同意、去重、审计留痕)是**默认幸存者**,另一侧 + **改绑到它并删除** —— 不是两侧对齐,也不是保留双写。反向裁只在**产品语义明确要求**时 + 成立,且必须把那条语义写进裁决正文,而不是默认成立后再补理由。出处:cloud#896 + (hostname)即此形定案;cloud#1147 的三个待答问题(重装 = UPSERT、卸载 = 软停用、外部 + 词表 = manifest id)按本条全部落在治理侧;objectstack#4636 的 B 选项是同一形状。留着 + 未治理的那一侧等于给权限闸留一条旁路,而「声明即强制」要堵的正是那条旁路。 + Whenever a dev returns `needs_decision` that passes the bar above, an issue is too vague to dispatch, or rework has failed twice: -1. **Default: the decision lives ON the issue it belongs to — never a new +1. **先刷新卡片的前提 —— 落卡与复升级都适用。** 决策卡写下的每条前提(某个在飞 + PR 还没合、某能力还不存在、某文件还是那个形状)都是**有保质期的读数**:main + 一天 ~18 合并,跨仓事实按小时变。落卡**之前**、以及把一张旧卡重新推到维护者 + 面前**之前**,逐条复核一遍,失效的就地改写或撤卡 —— **隔夜没动过的卡,默认按 + 「前提未经验证」处理,不是按「还在等答复」处理。** 出处是决策箱第 2 轮的实测: + cloud#1148 的 A/B 卡在**写下前 ~50 分钟**就已失效(它等的那个上游 PR 已经合了), + cloud#812 一张卡带三条过时前提。前提过期的卡比没有卡更贵 —— 维护者会照着一个 + 不存在的世界做裁决,而卡面上没有任何读数会显示这件事发生过。 +2. **Default: the decision lives ON the issue it belongs to — never a new issue.** Post the analysis as a comment on that issue, add the `needs-user-decision` label, drop it from the active queue. The label is the maintainer's inbox (filter `label:needs-user-decision` shows @@ -1832,7 +1863,7 @@ is too vague to dispatch, or rework has failed twice: when the decision has no natural anchor — it spans several issues (file one, link it from each rather than duplicating the analysis) or arose with no issue of its own. -2. The analysis, wherever it lands (English, per the language policy): +3. The analysis, wherever it lands (English, per the language policy): background / the concrete question / options / your recommendation / related issues, PRs, branches。**每个方案必须沿三条固定评估轴 分析,这是决策分析的核心原则,不是可选项:** @@ -1858,7 +1889,7 @@ is too vague to dispatch, or rework has failed twice: 绝不让 AI 能声明一个运行时不兑现的能力。 推荐意见必须基于这三条轴给出理由;三轴冲突时如实呈现权衡,交维护者 拍板。 -3. If the session is interactive, additionally raise it via `AskUserQuestion`; +4. If the session is interactive, additionally raise it via `AskUserQuestion`; the labeled issue remains the durable record either way. **Never** answer a product/architecture question on the maintainer's behalf. diff --git a/skills/objectstack-pm-dispatch/SKILL.md b/skills/objectstack-pm-dispatch/SKILL.md index 6b05131100..8b5c79f4ab 100644 --- a/skills/objectstack-pm-dispatch/SKILL.md +++ b/skills/objectstack-pm-dispatch/SKILL.md @@ -439,13 +439,49 @@ Named non-escalation classes — act immediately: - **Sequencing and dependency ordering** between technical tasks. - **Verification strategy** — what regression pass a risky-but-decided change needs. That is scoping the work, not deciding it. +- **A declaration silently dropped on a new arm, when a sibling arm already has + a ruling.** When a declared key is silently ignored on one arm of a component + and an earlier ruling already made that same key a **loud authoring error** on + a sibling arm, the new arm **joins the existing rejection set by default** — + reuse that ruling, queue it, dispatch it; do not spend a fresh decision on a + one-word extension of an answer you already have. Only a genuine **semantic + difference between the arms** reopens the question. **State the boundary in the + same breath as the default, or the shortcut gets over-applied:** what carries + over is the ruling **together with its rationale**, never the verdict alone. + When the original rationale was measured to be arm-specific — it held on the + face it was ruled on and is disproved on the new one, as a "refusing this would + reject usage that already works" argument does the moment the new arm has no + such working usage — the default does not apply and the new arm earns its own + decision. The test is mechanical: re-check the original rationale against the + new arm *before* reusing the verdict. Sharing a key name is not sharing a + reason. +- **Two implementations of one operation.** When one operation has two + implementations and they disagree, the side that already carries the + **governance** — authorization gates, user consent, de-duplication, audit + trail — is the **default survivor**; the other is rebound onto it and deleted. + Not reconciled, not kept as a second writer. The ungoverned side wins only + when **product semantics explicitly demand** it, and that semantic goes into + the decision text rather than being supplied afterwards as justification. + Keeping both implementations keeps a path around the gates — exactly what + `declared = enforced` exists to close. - A developer agent's `needs_decision` that, on review, falls into the classes above: answer it yourself with the decision and rationale; do not relay it upward. When something *does* pass the bar: -1. **The decision lives ON the issue it belongs to — never a new issue.** Post +1. **Refresh the card's premises first — before writing it, and again before + re-escalating it.** Every premise a decision card states (an in-flight change + has not landed, a capability does not exist yet, a file still has this shape) + is a **reading with a shelf life**: an active main branch takes on the order + of a dozen or more merges a day, and cross-repository facts move on an hourly + scale. Re-check every premise immediately before posting the card — and again + before pushing an older card back in front of the maintainer — then rewrite or + withdraw whatever no longer holds. **A card that sat overnight untouched is a + card whose premises are unverified, not merely a card that is waiting.** An + expired premise is worse than no card: the maintainer rules on a world that no + longer exists, and nothing on the card shows that this happened. +2. **The decision lives ON the issue it belongs to — never a new issue.** Post the analysis as a comment there, add `needs-user-decision`, drop the issue from the active queue. The label is the maintainer's inbox (filter `label:needs-user-decision`); when they answer, the label comes off and the @@ -453,10 +489,10 @@ When something *does* pass the bar: `[Decision] `, same label) ONLY when the decision has no natural anchor — it spans several issues, or arose with no issue of its own. -2. Write the analysis with: background, the precise question, the options, your +3. Write the analysis with: background, the precise question, the options, your recommendation, and the related issues / PRs / branches — **and analyze every option on the three fixed axes below.** -3. If the session is interactive, additionally ask the maintainer directly; the +4. If the session is interactive, additionally ask the maintainer directly; the labeled issue remains the durable record either way. **Never** answer a product or architecture question on the maintainer's behalf.