diff --git a/.changeset/pm-findings-discipline.md b/.changeset/pm-findings-discipline.md new file mode 100644 index 0000000000..65328706d1 --- /dev/null +++ b/.changeset/pm-findings-discipline.md @@ -0,0 +1,19 @@ +--- +--- + +docs(pm-dispatch,os-dev): 发现类 issue 纪律 —— 查重、归挂、`finding` 分级与分诊出水口 (#4949) + +维护者观察「issue 越开发越多」后的复盘产物(cloud 分片一日数据:关 14 开 19)。 +病根不在 Prime Directive #10(发现即立单)—— 返工全部来自大而含糊/过时的 +issue,小而精确的单派发成功率近 100% —— 而在循环只有生产者、没有出水口。 + +- **os-dev 立单纪律三条**:立单前按关键词 + 文件路径查重(cloud#1054 与 + cloud#1031 同日重复);属已排队 issue 完成范围的开 sub-issue、仅依赖的 + 独立立单 + `Blocked-by:`;观察类打 `finding` 标签、不自行判级压单 + (立单时判级最不准:cloud#1004 的「转义细节」实为 P0 过滤器旁路)。 +- **SKILL.md**:setup 增 `finding` 标签;step 0 分类增 Hold 类(评论/台账 + 是 ADR-0049 的 silent state 与 rule 5 的 second tracker,记录必须留在 + issue 层);新增**发现分诊轮** —— 每 ~5 轮批量过一遍 `finding`,晋级 / + 关闭 not planned(否决窗口)/ 持有,判级责任归此轮;轮次报告增三个有界 + 健康度指标(可派发库存、决策收件箱、`finding` 中位年龄),总 open 数 + 刻意不设为指标。 diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 5f351b529b..d23eea6f6d 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -26,7 +26,24 @@ rules that most often get missed: with someone else's in-flight work, stop and report `blocked`. 3. **Scope = the issue. Nothing else.** Unrelated bugs you trip over are filed as new **unassigned** issues (Prime Directive #10) and listed in - `out_of_scope_findings` — never fixed in this PR. + `out_of_scope_findings` — never fixed in this PR. Filing discipline + (objectstack#4949): + - **Search before filing.** Keyword + file-path search over open issues + first; on a hit, comment there instead of opening a twin. Parallel devs + cannot see each other's same-hour filings (cloud#1054 duplicated + cloud#1031); the PM race-closes stragglers, but the search is yours. + - **Attach, don't scatter.** A finding whose fix falls **inside the + completion scope** of an already-queued issue is filed as that issue's + sub-issue; one that merely *depends* on it is filed standalone with a + `Blocked-by:` line (cloud#1045/#1046 depended on cloud#1050 — standalone + was right; a sub-issue of a queued parent auto-enters the dispatch pool). + - **Observation-class findings** — dormant code, unexercised drift, + cosmetic polish, nothing a user hits today — get the `finding` label and + NO `pm:queue`. Concrete defects stay unlabeled for PM triage. Never sit + on a finding because it "seems small": severity judged at filing time is + unreliable in both directions (cloud#1004's "escaping detail" was a P0 + filter bypass; cloud#897's own impact section was wrong). File it plainly + and let the PM's triage round grade it. 4. **Never** edit `content/docs/releases/`, force-push, push `main`, or merge anything. User-visible changes need a `.changeset/*.md`. 5. **Contract-first.** If the fix tempts you to add a lenient fallback in a diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index baeaa47789..db2f2cb395 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -59,6 +59,7 @@ for R in objectstack-ai/objectstack objectstack-ai/objectui objectstack-ai/cloud gh label create pm:queue -R "$R" -c 0e8a16 -d "Ready for the PM dispatch loop" || true gh label create pm:dispatched -R "$R" -c 1d76db -d "Dispatched to a dev agent by /pm-dispatch" || true gh label create needs-user-decision -R "$R" -c d93f0b -d "Blocked on a maintainer decision — do not dispatch" || true + gh label create finding -R "$R" -c c2e0c6 -d "Recorded observation — held, not dispatchable until the findings triage round grades it" || true done # routing labels exist only on the main backlog repo: gh label create repo:objectui -R objectstack-ai/objectstack -c fbca04 -d "Lands in objectui (frontend)" || true @@ -377,10 +378,40 @@ label and classify each: sequencing, anything touching stored-data migration shape or removing a shipped capability. The label alone is the inbox entry; the deep two-axis analysis is written when the card is actually taken up. +- **Hold (`finding`)**: observation-class findings — dormant code, + unexercised drift, cosmetic polish; real, but nothing a user hits today. + The label keeps the record **inside the state machine** (a comment thread + or a side-ledger issue is exactly the silent state ADR-0049 bans, and a + second tracker rule 5 prohibits) without occupying the dispatch queue or + the maintainer's inbox. It is a held state, not a verdict — the findings + triage round below is where it gets one. - **Repair first**: a body truncated by GitHub's sanitizer (bare `` swallows the rest at rest) cannot be dispatched — comment the repair instruction and move on. +#### 发现分诊轮 —— 队列的出水口(objectstack#4949) + +Prime Directive #10 是一个强力生产者,而循环原本只有「修掉」和「关重复」两条 +出路 —— 没有任何机制裁定「不值得修」,总量于是只涨不落(cloud 分片一日实测: +关 14 开 19,其中真缺陷当天闭环,涨出来的主要是观察类)。分诊轮补上出水口: + +- **触发**:每 ~5 轮一次;`finding` 存量超过 ~15 时插队执行。 +- **动作**:对每条 `finding`(以及久悬未分类的观察类 issue)先做过时前提 + 检查 —— main 一天 ~18 合并,发现的准确性半衰期以天计,囤积不是免费存档, + 是负利息存款 —— 然后三选一: + 1. **晋级**:摘 `finding` 换 `pm:queue`,进正常派发; + 2. **关闭(not planned)**:附一句理由,列入当轮轮次报告 —— 维护者可 + 否决重开,但 PM 不等批准(否决窗口,同 step 8 的模式); + 3. **持有**:留标签,记一行「为什么还留着」。 +- **判级发生在这里,不发生在立单时。** 立单的 dev 只有局部视野,判级最不准 + (cloud#1004 的「转义细节」实为 P0 过滤器旁路;cloud#897 自己写的影响面 + 评估就是错的)。所以 os-dev 的纪律是「照实立单、不自行压级」,分级的责任 + 归本轮。 +- **归挂限定**:发现开成已排队 issue 的 sub-issue **仅当**它真属父单完成 + 范围 —— 已排队父单的 sub-issue 会自动成为派发候选(step 1),把仅有依赖 + 关系的发现挂进去等于让未分诊的东西静默入池;那种情况独立立单 + + `Blocked-by:`(cloud#1045/#1046 之于 cloud#1050 即此形)。 + ### 1. Fetch candidates List open issues matching the filter, excluding anything assigned or labeled @@ -818,6 +849,14 @@ issue → verdict → PR link → notes, plus anything escalated. Then start the next round at step 1 (rework re-dispatches count against the next round's `batch` budget). +Track three **bounded** health metrics in the report — total open-issue count +is deliberately NOT one of them (discovery outruns closure in any debt-dense +area; that is the loop working, not failing): + +- **dispatchable inventory** — open `pm:queue` unassigned, and its trend; +- **decision inbox** — `needs-user-decision` count awaiting the maintainer; +- **finding median age** — aging findings mean the 发现分诊轮 is overdue. + ## Stop conditions Stop the loop and report when any of these hits: @@ -861,5 +900,9 @@ Stop the loop and report when any of these hits: `open_questions` must be non-empty when `status` is `needs_decision`, and each entry becomes input to a `[决策]` issue. `out_of_scope_findings` should already -be filed as unassigned issues by the dev (Prime Directive #10) — the PM only -verifies they exist. +be filed as unassigned issues by the dev per the filing discipline in the +os-dev definition (search-first, sub-issue attachment, `finding` labeling — +objectstack#4949). The PM verifies they exist **and cross-checks the round's +parallel reports against each other**: two devs auditing adjacent code file +twins within the hour (cloud#1054 duplicated cloud#1031 same-day), and only +the PM sees both reports.