Skip to content

skills(pm-dispatch): judge responsibility before dispatch, and stop a PR whose security review keeps reopening #21929

Description

@objectstack-fleet

Requested by the maintainer in Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-06, verbatim: 「以后怎么避免在这类开发上耗资源,创建一张skills卡」.

What happened

#21840 (P2, readable only by platform administrators, and affecting only plugin drivers, of which the repository has none) was dispatched as a security fix. PR #21877 tried to decide, from key names and value shapes, which values in a plugin driver's config are credentials.

The cost came before any code was written: nobody asked whose problem it was, and nothing stopped the loop once it started.

Rules to add to .claude/skills/pm-dispatch/

  1. Responsibility check before dispatch. Before dispatching a defect, the seat answers three questions in the claim or the dispatch:

    • Whose code produces the risk?
    • Does the platform already provide the right path, such as a bound secret, a declared contract or a documented boundary?
    • Who can reach it, and does anyone use it today?

    When the risk sits in third-party or user-authored code, a supported path exists, and only the highest privilege reaches it, the default is document, not defend. The seat proposes a docs change or a decision card, and does not dispatch a code fix.

  2. No security boundary built on a heuristic. If the proposed fix decides security by guessing (key names, value shapes, pattern lists), the seat stops and opens a decision card that compares it with a declaration-based approach, before dispatch.

  3. Circuit breaker on a PR. The seat pauses the PR and asks the maintainer, with a short summary and options, when any of these holds:

    • the independent security review has failed two rounds in a row;
    • the diff has grown past twice its first reviewed size;
    • a review finds a new HIGH that was introduced by the previous round's fix.

    No further development round is dispatched until the maintainer answers.

  4. Price by reach, not by class. A finding with no current user, reachable only by the highest privilege, is graded at most low and may be recorded without a fix. The "security" label alone does not raise its priority.

Done when

  • The four rules are in the pm-dispatch skill text, at the point where the seat claims or dispatches, and where it handles review rounds.
  • The dispatch prompt template asks for the three answers in rule 1.
  • The round-report shape has a line that records which circuit-breaker condition, if any, a PR has hit.

维护者速读

#21840 只影响仓库里不存在的插件驱动,而且只有平台管理员能看到,却被当成安全漏洞派了开发。#21877 靠猜字段名,复审 4 轮都没收住,代码长到 4000 行,最后作废,改成补一句文档。

要在派发规则里加四条:

  1. 派发前先判断责任归属和影响面。属于作者代码、平台已有正路、又只有最高权限能触及的,默认只补文档,不写防护代码。
  2. 方案靠"猜"来做安全判断的,先开决策卡。
  3. 熔断:安全复审连续 2 轮不过、改动超过最初规模的 2 倍、或者修复本身引出新的高危,满足任一条就暂停,来问你。
  4. 优先级按实际影响面定,不因为带了"安全"标签就自动拔高。

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: fleet decision — the dispatch loop spends dev effort where it lands value | 缺项 | none

    Triage: first grade — tooling · priority:p2 · domain:skills · pm:queue. Three rules for pm-dispatch, on the maintainer's order

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-06T01:56Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in .claude/skills/pm-dispatch/ (the dispatch and review references) ⇒ domain:skills; rationale: it changes the instruction set every dispatching seat reads, which is the named surface that admits this tooling card to the queue.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_0181E4ZeZmWyknawnauxD2CE
    Account: os-steve (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-21929-judge-responsibility-before-dispatch
    Worktree: objectstack-issue-21929
    Domain: domain:skills
    Seat: domain:skills#2
    File surface: .claude/skills/pm-dispatch/SKILL.md (the 〈升级与决策〉 escalation-criteria line and the claim template in 〈模板与表〉), .claude/skills/pm-dispatch/references/execution-duties.md (〈候选与批次〉 / 〈派发〉 / 〈复核〉 — the REWORK-cap line), .claude/skills/pm-dispatch/references/dispatch-runbook.md (〈派发词构造细则〉), .claude/skills/pm-dispatch/references/seat-lifecycle.md (the 〈轮次报告与节奏〉 report line), .claude/skills/pm-dispatch/references/triage-duties.md (the first-grade question), .claude/skills/pm-dispatch/references/filing-gate.md (〈定级判据〉, reach) — region-level; every file is at its ratchet ceiling (headroom 0 at origin/main aa09db58c9), each added line paid in place. ⛔ .claude/agents/os-dev.md, skills/**, scripts/**, .github/** untouched; stop on breach; explain in the report
    Container & model: M, mode:subagent, model: CONTRACT_REVIEW_TIER — dispatch-gates --tier --repo objectstack-ai/objectstack .claude/skills/pm-dispatch/SKILL.md … prints "Model tier — MANDATORY … (derived from the file surface, not recalled)" with the path line ".claude/skills/pm-dispatch/SKILL.md ⇢ '.claude/skills/pm-dispatch/SKILL.md' — clause ① of the model-tiering ruling (narrowed to protocol semantics, 2026-08-20)"; no downgrade exit taken: the card rewrites protocol semantics across six files, not a one-line mechanical edit
    Clause-②: no
    Thread-read: 6007745609
    Serial constraints cleared: none — 4 open PRs at the reading — #21994 · #21991 · #21988 · #21974 — file lists read through REST, none on .claude/**, scripts/pm/**, package.json, .github/workflows/lint.yml or docs/audits/**; git ls-remote shows no claude/issue-21929-* branch; the last touches of the surface on origin/main are d13df0c6 and e27a7c0c (2026-10-05), not same-day; seat 1's hot-file table (#7623 r139) names only dispatch-gates.mjs FROZEN and check-widening-tells.mjs, and its in-flight #21892 is on scripts/pm/**; the sibling claim this round (#21959) touches references/instrument-discipline.md line 7 only, a file this card does not name; verify lock state: lock is free queue: empty — arrival depth 1 (this dev), below LOCK_DEPTH_HOLD; shared checkout at origin/main aa09db58c9. Readings taken at 2026-10-06T14:09Z.

  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21929,
      "status": "done",
      "branch": "claude/issue-21929-judge-responsibility-before-dispatch",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21999",
      "session": "session_0181E4ZeZmWyknawnauxD2CE",
      "premise_still_valid": true,
      "summary": "Four rules landed in .claude/skills/pm-dispatch/ (5 files, 8 lines added or rewritten, 8 deleted, every file at its ceiling before and after): the claim template gains a Responsibility: field carrying the three questions (whose code / platform path / who reaches it, used today) and binds the dispatch prompt's ruling section to repeat them; the escalation enumeration in SKILL.md 升级与决策 gains the document-not-defend exit (rule 1 verdict) and the no-heuristic-security-boundary exit (rule 2); the ESCALATE verdict line in execution-duties 复核 now fires on any of three breaker conditions (security review failed 2 rounds in a row / diff past 2x first reviewed size / previous round's fix introduced a new HIGH) with PR paused and no next round until the maintainer answers; seat-lifecycle's round report lists breaker hits per PR; triage asks the three questions at first touch; filing-gate caps a highest-privilege-only, no-current-user finding at p3 regardless of the security label. Premise verified at base 6befe19c: zero hits for the rule vocabulary across the skill with a live control word. Draft PR 21999, Tier S (.claude/** only), awaiting the skills seat's contract-tier review; head 8cdefa6c.",
      "tests": "No package touched, so no build/test/typecheck owed. Gates derived in the worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 8cdefa6c: 21 commands (identical to the dispatch list); all 21 run on head 8cdefa6c with exit captured before any pipe; --ran reconciliation: '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)'. Evidence: 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)', SKILL.md widest table row pin 342 unchanged; check:pm-skill-id-lint '34 file(s) clean'; check:skill-frame-sync 'the one declared copy of the decision frame is internally coherent'; frame block md5 abff5f852c8fd09b79ec0623439aa4f0 before and after; check:pm-governed-prose '2 instruction surface(s) name all 6 registered governed surfaces'; check:nul-bytes 'no raw ASCII control bytes'; check:doc-authoring '0 pinned site(s)'; check:comment-mask-corpus '8303 files, 0 disagree'. First attempt of pnpm --filter @objectstack/lint run check:doc-formula-expressions exited 3 (PREREQUISITE NOT MET: formula and lint not built) — NOT a measurement; built both with turbo under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 133s, waited 0s) and re-ran: exit 0, '22 record-scoped formula example(s) across 461 files / 1384 TS blocks judged clean'. New/rewritten prose line widths (LC_ALL=C awk bytes): 120, 108, 117, 110, 114, 113, 112; the template field is inside a fence (exempt). No ablation applies (prose only).",
      "mcp_calls": "0 — no MCP GitHub tool was called; reads went through gh api GET (issue, comments, PR read-back, label existence); writes only through scripts/pm/ (fleet-write relay).",
      "api_writes": "3 relay dispatches, 4 REST write actions executed by the relay as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (PR 21999, body read back identical, 14901 bytes); (2) label-write.mjs → labels_add POST /repos/objectstack-ai/objectstack/issues/21999/labels (skip-changeset) + assign POST /repos/objectstack-ai/objectstack/issues/21999/assignees (os-steve), read back MATCHES; (3) comment → POST /repos/objectstack-ai/objectstack/issues/21929/comments (this report). Plus 2 git push (not REST): the empty branch probe and commit 8cdefa6c.",
      "gates": {
        "derived_at": "8cdefa6c",
        "derived": 21,
        "run": 21,
        "not_measured": 0,
        "unrun": 0,
        "all_exit_0": true,
        "rerun_after_prerequisite": "pnpm --filter @objectstack/lint run check:doc-formula-expressions (first exit 3 PREREQUISITE NOT MET → build under verify lock → exit 0)",
        "not_in_derived_set_but_relevant": "check:skill-frame-freshness — frame block byte-identical to origin/main (md5 abff5f852c8fd09b79ec0623439aa4f0)",
        "ci": "in_progress — not awaited, per contract"
      },
      "line_budget": {
        "budget_rule_lines_max": 8,
        "spent": "8 (5 new: SKILL.md rule-1 verdict, SKILL.md rule-2 condition, SKILL.md Responsibility template field, execution-duties breaker conditions, filing-gate reach cap; 3 rewritten: execution-duties ESCALATE verdict, seat-lifecycle round-report line, triage-duties first-touch line)",
        "paid_by_deletions": "5 (SKILL.md reach restatement in 报告契约; SKILL.md 机械守卫索引 rows for git-history.mjs and the two guard hooks — all three survive in AGENTS.md / filing-gate.md / the kept 收口卡 line; execution-duties self-declared Clause-② minor restatement — survives in AGENTS.md Post-Task Checklist 3; filing-gate 判例 example — derivable from lines 12–13 and 20); survivors listed per line in the PR body",
        "files": {
          ".claude/skills/pm-dispatch/SKILL.md": "319 → 319 (ceiling 319)",
          ".claude/skills/pm-dispatch/references/execution-duties.md": "183 → 183 (ceiling 183)",
          ".claude/skills/pm-dispatch/references/dispatch-runbook.md": "241 → 241 (not edited)",
          ".claude/skills/pm-dispatch/references/seat-lifecycle.md": "96 → 96 (ceiling 96)",
          ".claude/skills/pm-dispatch/references/triage-duties.md": "120 → 120 (ceiling 120)",
          ".claude/skills/pm-dispatch/references/filing-gate.md": "58 → 58 (ceiling 58)"
        },
        "ceilings_changed": false,
        "rewrap_used": false
      },
      "files_changed": [
        ".claude/skills/pm-dispatch/SKILL.md",
        ".claude/skills/pm-dispatch/references/execution-duties.md",
        ".claude/skills/pm-dispatch/references/filing-gate.md",
        ".claude/skills/pm-dispatch/references/seat-lifecycle.md",
        ".claude/skills/pm-dispatch/references/triage-duties.md"
      ],
      "deviations": [
        "dispatch-runbook.md 〈派发词构造细则〉 was in the claimed file surface but is not edited: the Responsibility: template field's parenthetical already binds the dispatch prompt's ruling section to repeat the three answers, and a second line there (or a rewrite of execution-duties 候选与批次 line 13) would have cost a deletion for the same reader; reason in the PR body.",
        "PM mechanism hypothesis falsified by measurement: rule 2 cannot be appended to the 「只在至少一条成立时升级」 line (120 B) nor split into 「或修复需破坏性/难回滚动作」 (117 B); it lands as one 108 B condition line inside the same enumeration, between those two. Rule 3 likewise: the REWORK line is 118 B, so the breaker rides the rewritten ESCALATE verdict line plus one conditions line in 复核; no new section.",
        "'at most low' is spelled 至多 p3 (the repo's lowest grade, the one triage-duties already gives a reach-less finding); 'third-party or user-authored code' is spelled 他人代码 to fit 120 bytes.",
        "origin/main advanced aa09db58 → 6befe19c between dispatch and worktree creation; the branch is based on 6befe19c (one commit, fix(metadata-protocol), touching none of the surface).",
        "The harness attribution reminder names a model in its Co-Authored-By trailer; the commit carries the AGENTS.md model-free pair instead (Claude-Session URL + Co-authored-by: Claude). Reported, not a history rewrite."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none — noted, not filed · references/lanes/hotcrm.md 30 and references/lanes/engine.md 32 carry lane-local 'ask where the producer is' lines for declared≠enforced cards; the new first-touch three-question line generalises them; no conflict, lane files outside this card's surface.",
        "carrier: none — noted, not filed · 安全/权限边界 was already on the auto-adjudication human floor (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."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21999 (8cdefa6c), Tier S governed text; the contract-review record (PASS) is on the PR thread · session_0181E4ZeZmWyknawnauxD2CE (domain:skills#2) · 2026-10-06T14:51Z

    Checklist, read on GitHub: draft, base main, first line Fixes #21929 (the merge should close this card), Clause-②: no at body line 2, assignee os-steve, labels skip-changeset · documentation · size/s; files = 5 under .claude/skills/pm-dispatch/, +8 / −8 against merge-base 6befe19c, no content/docs/releases/, no unrelated file; no package touched ⇒ no changeset owed; 「维护者速读(草稿)」 present; no model id in the body or the commit trailers. Report comment 6018766887 on this card (first line os-dev-report, 8389 bytes). mcp_calls 0; api_writes 3 relay dispatches (pr_create, labels + assignee, report) — no write tool, no PATCH of the PR body.

    Spot-checks: every added line is at most 120 bytes (one exactly 120); no #NNN appears in the diff; the five deletions each have a verified surviving home (listed in the review ①); the frame block md5 is unchanged; the new Responsibility: claim key is inert to the Claim: / Thread-read: readers in check-half-states.mjs. Check-runs on the head at this reading: 30 completed (success / expected skip), 3 in progress (Lint & Repo Gates, Type Check · consumer gates, Type Check · debt ledger); the enqueue waits for them, then check-expected-skips --pr 21999 exit 0 and check-governed-merges --pr 21999 reading Tier S.

    Deviations: five, all accepted (review ③). Acceptance notes: 2 items with carrier: none — Acceptance notes, not filed. Rule 4 (reach caps a highest-privilege-only, no-current-user finding at p3) is the one line that generalises past the verbatim #21921 rulings; it is the card's rule as filed and graded, and the 速读 draft in the PR body is the maintainer's veto window — named in the round report.

    Landing: Tier S ⇒ this seat lands it through the queue once the three checks complete green (pr_ready + automerge_enable through the relay). Sibling #21992 edits the same two files in other regions and has been told which lines this PR touches.

  5. added 2 commits that reference this issue on Oct 7, 2026
    1bc6ca1
    803764a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions