Repository navigation
docs(pm-skill): judge responsibility before dispatch, breaker on reopening security review - #21999
Conversation
…ening security review Four rules land in the pm-dispatch protocol text, each paid in place against the line ratchet: the claim template gains a `Responsibility:` line (whose code / platform path / who reaches it), the escalation enumeration gains the document-not-defend exit and the no-heuristic-security-boundary exit, the REWORK/ESCALATE verdicts gain the three circuit-breaker conditions, the round report lists breaker hits per PR, triage asks the three questions at first touch, and the grading rules cap a highest-privilege-only, no-current-user finding at p3 regardless of the security label. Claude-Session: https://claude.ai/code/session_0181E4ZeZmWyknawnauxD2CE Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Face reviewed: governed rule text ( ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #21929
Clause-②: no
What changes
Five files under
.claude/skills/pm-dispatch/, eight lines added or rewritten, eight lines deleted, every file at its ratchet ceiling before and after. The card's four rules land where the seat claims, dispatches, grades and reviews; the dispatch prompt's ruling section is bound through the claim template; the round report gains a per-PR breaker line. No gate, workflow, hook or ratchet is added or moved, as the triage grade asked..claude/agents/os-dev.md,skills/**,scripts/**and.github/**are untouched.SKILL.md〈模板与表〉 claim template, underClause-②Responsibility:— whose code produces the risk / the platform path that already covers it, or none / who reaches it and whether anyone uses it today;n/a — not a defect cardotherwise; the parenthetical binds the dispatch prompt's ruling section to repeat the three answersSKILL.md〈升级与决策〉 :214或责任三答齐指他人代码、已有正路、仅最高权限可达 ⇒ 只提文档或决策卡,⛔ 不派码。references/triage-duties.md:74首触答责任三问:谁的代码出险/有正路否/谁可达且有人用否;据答改卡范围、域与路由。SKILL.md〈升级与决策〉 :215或修复以猜测(键名、值形状、模式表)定安全边界 ⇒ 决策卡先比对声明式方案。references/execution-duties.md〈复核〉 :182判决 ESCALATE:见〈升级与决策〉;熔断任一命中亦判,PR 暂停,答复前 ⛔ 不派下一轮。references/execution-duties.md〈复核〉 :183熔断三条:独立安全复审连续 2 轮不过;diff 超首次复核规模 2 倍;上轮修复引入新 HIGH。references/seat-lifecycle.md〈轮次报告与节奏〉 :68报告加升级项、代裁清单(分诊)、awaiting a human merge 项、逐 PR 熔断命中项(无则 none)。references/filing-gate.md〈定级判据〉 :43可达面定上界:\security` 标签不抬级;仅最高权限可达且无现用者至多 p3,可记录不修。`Reading the escalation enumeration after the edit (SKILL.md 213–216): 只在至少一条成立时升级 → the existing disagreement condition → rule 1's verdict → rule 2's condition → the existing destructive/hard-to-roll-back condition with its 「其余归 PM 裁量」 closer. The decision-frame block (
**每个方案必须沿四条固定评估轴分析…交维护者拍板。) is byte-identical; its md5 isabff5f852c8fd09b79ec0623439aa4f0before and after.Why these spellings:
execution-duties.md, because both the triage seat and the execution seats read SKILL.md, and the verdict is a "do not dispatch code" decision — the same class as the enumeration it joins. ESCALATE already means a decision card with options and a recommendation in the maintainer's inbox, and aneeds-user-decisioncard is never dispatched (红线), so "asks the maintainer with a short summary and options" and "no further round until the maintainer answers" ride on existing machinery; the ESCALATE line still saysPR 暂停,答复前 ⛔ 不派下一轮so the seat does not have to infer it.至多 p3: p3 is this repo's lowest grade and the gradetriage-duties.mdalready assigns to areach:-less finding that is recorded without a dedicated fix.可记录不修is the "may be recorded without a fix" half.他人代码stands for third-party or user-authored code (the first answer is "someone else's code produces the risk"); the long spelling did not fit in 120 bytes beside the other two answers.Line budget (headroom 0 on every file, net 0 on every file)
.claude/skills/pm-dispatch/SKILL.mdreferences/execution-duties.mdreferences/dispatch-runbook.md(not edited)references/seat-lifecycle.mdreferences/triage-duties.mdreferences/filing-gate.mdThe dispatch budget was at most 8 added or rewritten lines across all files: 8 were spent (5 new, 3 rewritten in place). Every new or rewritten prose line is at or under 120 bytes (
LC_ALL=C awk; the figures are in the table above); the one template field is inside a fence, which the rule exempts. No line was re-wrapped; each added line is paid by deleting a line whose rule survives elsewhere, listed here with its surviving home:SKILL.md〈报告契约〉:- \reach:` 无实测 ⇒ ⛔ 不立卡,例外三种与定义见立卡门 ①;同轮报告互读,同族只开一张。Survives inreferences/filing-gate.md立卡门 ① (lines 12–14: thereach:definition and the three exceptions; line 20:⛔ 不是卡:三类外或无 `reach:` 的 `finding`), inreferences/triage-duties.md47–48, and in the kept SKILL.md line同族发现并入收口卡(一卡覆盖全族位置,带枚举钉子),⛔ 不开单点卡;无则第二次即开。`; the kept line 「席位读 PR … 经立卡门补立」 still points at 立卡门.SKILL.md〈机械守卫索引〉 row forscripts/pm/git-history.mjs.Survives verbatim in
AGENTS.md(Multi-agent working discipline: "A windowed history question … goes throughscripts/pm/git-history.mjs— answer, or REFUSE …historyHorizon()is the read-only predicate"), and the tool's use is still named inreferences/seat-lifecycle.md19.SKILL.md〈机械守卫索引〉 row for theguard-main-checkout/guard-shared-stashhooks.Survives in
AGENTS.mdPrime Directive 11 and Multi-agent working discipline (both hooks, their escape variables and the stash replacements), and the rule itself in SKILL.md 红线⛔ 永不编辑共享检出,一任务一 worktree;⛔ 永不 \git stash`。`.references/execution-duties.md〈认领〉:- \Clause-②: yes` 至少 `minor`:AGENTS.md Post-Task Checklist 第 3 条,本行在认领处复述。A self-declared restatement; survives inAGENTS.mdPost-Task Checklist step 3 (yestakes at leastminor), which binds every seat, and is enforced on the PR by the changeset gate that reads the body'sClause-②` line.references/filing-gate.md立卡门:- 判例:自注「未测量」的 (b) 卡 ⇒ 不立;\reach:` 记公开入口一次实测错误的 (b) 卡 ⇒ 立。`A worked example of lines 12–13 and 20 in the same section, which stay; both halves of the example are derivable from them.
Also rewritten rather than deleted:
triage-duties.md74 (派发前按生产者的答案改卡的范围与域标签。) keeps its content inside the new line (据答改卡范围、域与路由, now at first touch), and line 73 keeps the declared≠enforced producer question unchanged.Dispatch hypotheses, measured
filing-gate.md" — confirmed for thereach:requirement; thesecurity-label and highest-privilege-only sentences were absent (at6befe19c,git grep -E '熔断|责任三|Responsibility:|声明式方案|不派码|不抬级'over.claude/skills/pm-dispatch/exits 1; the control升级与决策hits 6 files), so one line landed, placed as the sibling of 「沿链继承只给上界」 so the two upper-bound statements read together and neither conflicts with 清单项三态 ① inheritance.dispatch-runbook.md〈派发词构造细则〉 was named as a landing spot for "the dispatch prompt carries the three answers". Not edited: the claim template line already says the dispatch prompt's ruling section repeats the three answers, the PM writes the claim immediately before the prompt, and a second sentence in the runbook would have cost a deletion there for a line with the same reader.execution-duties.md〈候选与批次〉 line 13 (維护者裁决 → 派发词裁决分区) was not rewritten for the same reason.Acceptance notes
references/lanes/hotcrm.md30 andreferences/lanes/engine.md32 carry lane-local copies of the "ask where the producer is" question for declared≠enforced cards; the new three-question line generalises them for every card at first touch. Left as they are (lane files, not in this card's surface; no conflict). carrier: none — noted, not filed.AGENTS.mdlists安全/权限边界on the human floor for auto-adjudication (triage-duties 95–96, SKILL.md 262) but the escalation enumeration had no security-boundary trigger before this PR; rule 2 is that trigger, narrowed to fixes that decide the boundary by guessing. Observation only.Gates
Derived from this change set in the worktree with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat head8cdefa6c: 21 commands, the same 21 the dispatch named. All 21 ran on8cdefa6c(the tree was clean; the gates read the committed files) with the exit code captured before any pipe, and--ranreconciliation answered: "21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED (a DERIVED zero — all 21 recorded an exit code and none of them is 3)". One first attempt was not a measurement:pnpm --filter @objectstack/lint run check:doc-formula-expressionsexited 3 (PREREQUISITE NOT MET:@objectstack/formulaand@objectstack/lintwere not built in the fresh worktree); afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lintunder the shared verify lock (VERDICT command-exit 0, held 133 s) the gate re-ran and exited 0 ("22 record-scoped formula example(s) across 461 files / 1384 TS blocks judged clean"). Selected verdict lines:pnpm check:pm-skill-ratchet: "SKILL.md is 319 lines (ceiling 319; headroom 0)", "execution-duties.md is 183 lines (ceiling 183; headroom 0)", "seat-lifecycle.md is 96 lines (ceiling 96; headroom 0)", "triage-duties.md is 120 lines (ceiling 120; headroom 0)", "filing-gate.md is 58 lines (ceiling 58; headroom 0)"; the widest-table-row pin for SKILL.md stays at 342 bytes; no over-budget line (that red prints only on failure).pnpm check:pm-skill-id-lint: "34 file(s) clean (pattern /#[0-9]{3,}/g)".pnpm check:skill-frame-sync: "the one declared copy of the decision frame is internally coherent (.claude/skills/pm-dispatch/SKILL.md) … 4 axes … binding sentence present".pnpm check:pm-governed-prose: "2 instruction surface(s) name all 6 registered governed surfaces … and claim no others".pnpm check:nul-bytes: "scanned 10023 text file(s) … no raw ASCII control bytes".pnpm check:doc-authoring: "sibling-package prose ids hold the baseline — 0 pinned site(s)".check:skill-frame-freshnessis not in the derived set; it compares this tree's frame withorigin/main, and the frame block is byte-identical (md5 above). No package build, test or typecheck is owed: the diff touches no package. Changeset: none —.claude/**publishes nothing (fast lane), so the PR takesskip-changeset.Landing
Every path is under
.claude/**, so this is Tier S: the PR stays draft until the skills seat's contract-tier review record (Served-tier: CONTRACT_REVIEW_TIER, PASS) is on the thread or the card; the seat then lands it through the queue. No model identifier appears in this body or in the commit trailer pair.维护者速读(草稿)
改了什么:给 PM 派发技能加了四条规则,共改五个文件、八行,逐行用删掉的重复行付账,每个文件行数不变。① 认领模板多一行
Responsibility:,派发前先答三问:风险出自谁的代码、平台是否已有正路、谁能碰到且今天有没有人在用;派发词的裁决区照抄这三答。② 三答都指向「别人的代码、已有正路、只有最高权限能碰到」时,默认只补文档或开决策卡,不派代码修复。③ 修复靠猜(字段名、值形状、模式表)来划安全边界的,先开决策卡、对照声明式方案,再谈派发。④ 熔断三条:独立安全复审连续两轮不过、改动超过首次复核规模两倍、上一轮修复引出新的高危,任一命中即暂停 PR、升级问你,你答复前不派下一轮;轮次报告逐 PR 列出命中项。⑤ 定级按可达面:security标签本身不抬优先级;只有最高权限能碰到、又没人在用的发现至多 p3,可以只记录不修。分诊首触也要先答这三问。为什么改:上一次把一张只影响仓库里不存在的插件驱动、只有平台管理员能看到的卡当成安全漏洞派了开发,复审四轮都没收住,改动长到四千行,最后作废改成补一句文档。成本花在开工之前没人问「这是谁的问题」,和开工之后没有东西能停下循环。这五处改动把这两个问题各放在席位必经的那一行上。
风险与代价(含回滚):风险是规则写得过短被误读 —— 每行不超过 120 字节,只能用缩略说法(如「他人代码」指第三方或用户代码,「至多 p3」对应「最多 low」)。代价是删了五行重复文本,每一行的规则都在 AGENTS.md 或同技能的另一处保留,PR 正文逐条列了归宿。回滚是单个 commit 的 revert,不涉及代码、门禁或 workflow。
席位意见:(留空,由席位填写)
你要做的:看一眼五段速读里的四条规则是否与你在 #21921 上的裁决一致;一致则由 skills 席按达档复核落地,不需要你合并。
Generated by Claude Code