Repository navigation
Commit 2ec4f43
pm-dispatch(security): a finder reports a security finding through the security channel and never fixes it in place (#29939)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBQiV7K7TN7pm6wRVsEya1vr)_
Fixes #29731
Clause-②: no
Metadata-protocol: none
**Rules text only.** Two governed `.claude/**` files and the
line-ratchet map; nothing published moves (`skip-changeset`). No
finding, no mechanism and no example that encodes one is in this diff.
## 维护者速读
- **改了什么**:安全发现规则加一条「只报不修」:发现者不扩当前 PR 或分支、不自开修复分支、不在同一面顺手加固;dev
只经终报私有字段上报,席位与 QA runner 经中继建草稿公告再敲门;任务绕不开那条代码路径时报
`blocked`,只写一句不透明原因,席位停卡,不绕行;修复只由 `security#1` 席以无卡派发令派出。os-dev.md
只留一行指针。
- **为什么改**:你 2026-11-03
的原话「开发中遇到的问题应该通过安全通道上报而不是直接处理」。原文只写了安全发现写到哪,没写不能在手头任务里顺手修;就地修会把机制经
diff、PR 与 changeset 公开,也绕过了归属席。
- **风险与回滚**:只动规则文字,无代码无发布面;回滚即还原本 PR。`security-findings.md` 上限 62 →
64,按棘轮头部的裁决记录路径抬,配额注释引你的原话。
- **先看**:`security-findings.md` 第 60–64 行(§9,新规则),第 28
行(§3,修复只由该席派);`os-dev.md` 第 49 行(指针);`check-skill-line-ratchet.mjs` 的
CEILINGS 行。
- **两分钟验证**:`pnpm check:pm-skill-ratchet && pnpm
check:pm-skill-id-lint`,预期两条都 ✓(文件 64/64 行,每行 ≤120 字节)。
- **需要你做的**:无,席位落地(Tier S,`.claude/**` 内)。
## What changes
**`.claude/skills/pm-dispatch/references/security-findings.md`** (62 →
64 lines, every line ≤ 120 bytes):
- **§9 归属, the new rule (Wanted 1).** Line 60 now ends 「发现者只报不修」 in
place of the old 「⛔ 不归发现者」 (ownership by `security#1` plus "report,
never fix" says the same thing with one word fewer). A new line 61
states what "never fix in place" forbids: no widening of the PR or
branch in hand, no own fix branch, no hardening on the same surface; and
names the dev's channel, §5's private field. Line 62 is the old finder
line narrowed to the seat and QA runner: the relay `advisory_create`
draft plus the `Notify: security · GHSA-ID` doorbell carrying the id
alone. Line 63 (relay refused ⇒ `security#1` in session, maintainer
while vacant) is unchanged.
- **§9, the blocked rule (Wanted 2).** New line 64: when the task in
hand cannot proceed without that code path, the finder reports `blocked`
with the one opaque cause line 「a security item is with the owner seat」
and nothing else; the seat parks the card; ⛔ no workaround.
- **§3 line 28, folded.** 「修复经 pm-dispatch 以无卡派发令派 `os-dev`」 becomes
「修复只由 `security#1` 席以无卡派发令派 `os-dev`」 — the "fix comes only from the
owner seat's no-card order" clause of Wanted 1, carried by the line that
already described the fix dispatch, so §9 does not restate it.
**`.claude/agents/os-dev.md`** (405 lines, unchanged count): rule 3's
pointer line 49 is rewritten in place, 114 B → 116 B — 「安全发现 ⛔
不就地修,只经终报私有字段 `security_findings` 上报,照 `security-findings.md`」. The old
clause 「不进本字段」 is carried by 「只经 … 上报」 (only via the private field ⇒ not
via `out_of_scope_findings`, which §5 and the JSON template also state).
One pointer, no second copy (§8).
**`scripts/pm/check-skill-line-ratchet.mjs`**: the
`security-findings.md` CEILINGS row moves 62 → 64 with its row comment
quoting the ruling of record and the measured delta (next section).
### The ceiling raise, and why it is not paid by deletion
The order says fold first, pay by deletion second, raise only if the
ratchet header admits this card's verbatim maintainer word as the ruling
of record. Folding took two of the four sentences (dev's route into the
prohibition line; fix-only-by-order into §3 L28). The remaining two
rules — the prohibition itself and the blocked rule — are new facts no
existing line can carry under the 120-byte cap, and every line of the
file is a distinct rule, so there is no stale text to delete without
dropping a rule the card did not retire.
The header's raise path: "RAISING one is authorized by the RULING OF
RECORD that orders text into the file … the raising PR quotes that
ruling comment's id and the measured line delta in its body". The two
previous raises on this very row (55 → 56 and 56 → 62) cite the card
body filed under the maintainer's verbatim as that ruling. This card is
filed under gate ③ (a rule the maintainer stated) with the verbatim
「开发中遇到的问题应该通过安全通道上报而不是直接处理」; its body is the ruling of record, and the
row comment quotes it. **Raise: `security-findings.md` 62 → 64, measured
delta +2.** No other CEILINGS entry moves; not a `CROSS_FILE_MOVES`
destination, so no `ruledRaises` record applies. `os-dev.md` stays at
405/405 (one line rewritten in place).
## Why
`os-dev.md` L49 and `security-findings.md` §5 said *where* a dev writes
a security finding and nothing about not fixing it inside the task in
hand. A finder that fixes in place leaks the mechanism through the diff,
the PR and the changeset, and bypasses the owner seat that §9 (PR
#29695) made responsible from intake to publish. The maintainer's word
on 2026-11-03 is quoted above; this PR records it once, where the
finders read.
## Tests
None owed: prose in two `.claude/**` files and a data row in a gate
script's map. The gate script's own `--self-test` ran (Verification).
## Acceptance notes
- **Two choices a reviewer may push back on.** (1) 「任务绕不开它 ⇒ 报
`blocked`」 names no actor — the card says "the dev stops", but a seat or
QA runner meets the same wall, and 「dev 停下」 did not fit the byte cap
beside the fixed cause phrase. (2) The old 「⛔ 不归发现者」 on L60 is gone;
ownership by `security#1` plus 「发现者只报不修」 carries it.
- **A pre-existing inconsistency left alone (out of scope, carrier: the
skills seat).** §3 L25 and §4 L33 still route a relay refusal to the
maintainer directly (「机制只在会话里交维护者」), while §9 L63 (PR #29695) routes it
to `security#1` first and to the maintainer only while the seat is
vacant. Rule-level wording drift from the owner-section PR, not this
card's; noted, not filed. Dedupe words: security-findings §4 §9 relay
refused maintainer security#1.
- **Attribution thread link.** The order file spells the skills-lane
thread; this body carries this card thread's link because the session's
own attribution rule names it. Same project, same requester.
- **Changeset:** none — `scripts/pm/**` and `.claude/**` publish
nothing; `skip-changeset` sent on the `pr_create` stroke.
- Out-of-scope findings beyond the one above: none. Security findings:
none.
## Verification
Head: `c3118489` (branch `claude/project-thread-26bz62`, on
`origin/main` `21510bba`; the merge is a no-op, main did not move).
**Derived set:** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` → 40 commands (30 by path, 2 by change kind,
8 whole-tree). Every one ran in this worktree; exit codes captured
before any pipe into a `CMD :: exit N` file; `--ran` reconciliation: ✓
40 derived families accounted for — 40 run, 0 NOT-MEASURED.
- `pnpm check:pm-skill-ratchet` → exit 0 (security-findings.md 64/64,
os-dev.md 405/405; every line ≤ 120 bytes).
- `pnpm check:pm-skill-id-lint` → exit 0 (109 files clean).
- `pnpm check:skill-frame-sync` → exit 0.
- `node scripts/pm/check-skill-line-ratchet.mjs --self-test` → exit 0
(164 cases pass; the touched script's own suite, outside the derived
set).
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions` →
first run exit 3 (PREREQUISITE NOT MET: `@objectstack/formula` and
`@objectstack/lint` not built); after `turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint` under the
verify lock (VERDICT command-exit 0, 110 s) → exit 0.
- The other 35 derived commands (`check:pm-dispatch-gates`,
`check:pm-governed-merges`, `check:nul-bytes`, `check:doc-authoring`,
`check:commit-card-trailers`, the `--self-test` pairs, …) → exit 0 each;
the full `CMD :: exit N` list is in the `os-dev-report` comment's
`gates` field.
Not run locally, CI's own: `pnpm lint` (repo-wide), the type-check
lanes, `Test Core` / `Build Core`. No package test is owed: no package
source moved.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind. (no names
added)
- [ ] 3. No formatter ran; `pnpm lint` passes. (no formatter ran; the
repo-wide `pnpm lint` scan is CI's own run under the os-dev local-scope
rule, so it is not claimed here — read the PR's `Lint & Repo Gates`
check-run)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (publishes
nothing: `skip-changeset`)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the maintainer's
ruling is quoted verbatim, untranslated).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzfz49TMMdiSJVgkdAf)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ed0ab75 commit 2ec4f43
3 files changed
Lines changed: 14 additions & 6 deletions
File tree
- .claude
- agents
- skills/pm-dispatch/references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
62 | 63 | | |
| 64 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1332 | 1332 | | |
1333 | 1333 | | |
1334 | 1334 | | |
1335 | | - | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
1336 | 1342 | | |
1337 | 1343 | | |
1338 | 1344 | | |
| |||
0 commit comments