Skip to content

Commit 3868301

Browse files
committed
docs(pm-dispatch): the security lane — a security#1 seat owns every security finding, with its charter, the Notify: security doorbell and the seat signature
The maintainer set a `security#1` seat and amended it to a lane. This lands: the lane charter `references/lanes/security.md` (pointers only, the never-public rules stay in `security-findings.md`), the owner section in `security-findings.md`, the triage hand-over in `lanes/triage.md`, the `Notify: security · GHSA-ID` doorbell (`MAILBOX_KEYS` row, reader, self-test rows, protocol text), one 域车道 row in SKILL.md paid by deleting a 红线 that restated AGENTS.md Prime Directive #15, and `security` admitted to the seat signature's lane alternation in both of its copies, with a self-test row for `security#1 · …` and a non-lane word kept refused. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj
1 parent fc55892 commit 3868301

9 files changed

Lines changed: 108 additions & 14 deletions

File tree

‎.claude/skills/pm-dispatch/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
3030
- ⛔ 永不代维护者答产品或架构问题,⛔ 无代裁通道:席位只分析与推荐,裁决恒出自人工。
3131
- ⛔ GitHub 之外永不维护跟踪状态;看板(org Project)只是视图,权威层是 issue 正文 + REST。
3232
- ⛔ 永不跑版本发布、不合并 Version Packages PR;发布动作的完整清单以 AGENTS.md 为权威。
33-
- ⛔ 未经人工的发布痕迹按事故立案,不代跑补救性发布;机械通道存在不构成授权。
3433
- ⛔ 分诊席永不认领、派发或写码;执行席只在本车道认领,改 `domain:*` 仅限整树归本席。
3534
- 唯一例外:无主 p0 锚卡一轮即轮报、再一轮分诊只代派止血:每轮一张,复核非分诊。
3635
- ⛔ 报告缺席永不读作成功,永不虚构未回报 agent 的结果。
@@ -163,6 +162,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
163162
| `domain:spec` | `packages/spec` 整包:schema 形状、`contracts/**`、退役行为半边、strictness 台账;describe/JSDoc/墓碑散文/错误 guidance 与 alias 表;`packages/spec/scripts/**`、`packages/spec/docs/**` 及按锚定规则的例外归本域的工具链(域边界枚举与席内分派见 `references/lanes/spec.md`) |
164163
| `domain:ui` | `ui/**` 全部代码:`@object-ui/*` 库与 `ui/apps/*`、`ui/packages/types` 与 schema 语料、`ui/scripts` 与门禁工具、文档随所记录的面走;`packages/console`(dist 由 `pnpm console:build` 生成 ⛔ 不手改);协议恒归 `domain:spec`,受管面归 `domain:skills`;详见 `references/lanes/ui.md` |
165164
| `domain:cloud` | 涉 cloud 即整卡归 cloud PM(`repo:cloud` 席,同挂 `repo:cloud`):读或改 cloud 代码、生产方、事实或带 cloud 半边;本仓车道 ⛔ 不认领;cloud PM 跟到关卡,余下纯本仓活由它写明 cloud 事实后另立卡;判据见 `references/cross-repo-coordination.md` |
165+
| `security`(车道,无 `domain:*` 标签) | 安全发现(无文件面;修复经无卡派发令落在归属包);席位 `security#N`、接收、台账、派修与复核见 `references/lanes/security.md`;永不公开的规则只住 `references/security-findings.md` |
166166
| (无固定归属,按落点分诊) | `packages/apps/*`、`examples/*`(归它演练的子系统)、`docs/audits/**` |
167167

168168
- 表未覆盖的包首次分诊时归类并走 PR 更新本表;新增或退役 `domain:*` 必须同批改本表。
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# 车道章程:安全席(security)
2+
3+
见 seat-lifecycle.md;本文是本车道岗位说明,现值状态恒在座位贴,⛔ 不迁入本文。
4+
永不公开的规则只住 `../security-findings.md`(下文「第 N 节」皆指它),本文只指向、⛔ 不复述。
5+
本文与座位贴不载任何发现、编号、路径、对象名或机制。
6+
7+
## 座位
8+
9+
- 席位 `security#N`,首席 `security#1`;签名、座位贴、坐席与收班同他席。
10+
- 无 `domain:security` 标签、无 `pm:queue`、无文件面:安全发现永不成卡,修复落在归属包。
11+
- 坐席者 = 能读写本仓安全公告的账号(管理员或安全经理)的会话;读不了公告 ⛔ 不坐。
12+
- 读公告只经中继 `advisory_read`;建草稿与写公告只经中继,⛔ 永不以自身账号写。
13+
14+
## 接收与台账
15+
16+
- 接收 = 发现者的一次 `Notify: security · GHSA-ID` 门铃,草稿公告已由其经中继建好。
17+
- 中继拒 ⇒ 机制在会话里交本席;本席空缺 ⇒ 交维护者(第 3 节);⛔ 不换账号重发。
18+
- 台账住座位贴,一份一行:不透明编号 · 会话 · 状态;⛔ 无细节。
19+
- 状态词表:`received` → `fixing` → `merged` → `awaiting publish` → `published`。
20+
21+
## 派修与复核
22+
23+
- 修复 = 本席向 `os-dev` 下无卡派发令,`xhigh`;机制只进派发令与公告,⛔ 不进 PR 与评论。
24+
- 修复 PR 的契约复核归本席:记录停在规则层,机制只在本席本地的 `security-note.md`(第 6 节)。
25+
- 要定契约的修复交维护者;发布公告恒归维护者,本席把状态推到 `awaiting publish` 即止。
26+
27+
## 误公开的卡(第 7 节)
28+
29+
- 本席接管:挂 `security` 标签,⛔ 不追加细节;是否移入公告并关卡交维护者定,⛔ 不代关。
30+
- 分诊只即推维护者并转交本席(`triage.md`);本席空缺时分诊照第 7 节处置。
31+
32+
## 收班
33+
34+
- 台账与每份在飞公告留在座位贴,⛔ 不释放进 `pm:queue`;接任者坐席即接台账。

‎.claude/skills/pm-dispatch/references/lanes/triage.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@
1313
先敲其席(无信箱按名)过下轮无答且回收需其一句;待裁或被具名 `Blocked-by:`、`status:parked`
1414
卡、所骑栈阻者与信箱、Version PR 不算。⑦席位或子代理被拒、需人做的动作,⛔ 不代做。
1515
- 不推:定级、治半状态、解锁、契约复核(PASS/FAIL 归车道)、门铃答复、健康绿读数。
16+
- 安全发现:即推 ① 后转交 `security#1` 席即止(`security.md`);有公开卡只定 `security`+p1,余不判。
17+
- `security#1` 空缺时沿用 `security-findings.md` 第 7 节处置,⛔ 不代席、不追加细节。

‎.claude/skills/pm-dispatch/references/project-forms.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@
4444
- 移卡入队者→车道:p0/p1 `Blocked-by` 关闭 `Notify: unblocked`;记裁决席→被改席 `Notify: ruling`。
4545
- 席→兄弟席或他车道:认领文件面相交或须先落 `Notify: serial`,先落者落地 `Notify: landed`。
4646
- 上下线→兄弟席与分诊 `Notify: seat-online`、`Notify: seat-offline`;清单变→兄弟席 `Notify: split`。
47-
- ⛔ 不敲:回执、重发、催(逾期归升级);安全发现;收方订阅 PR 已载者;问维护者走决策卡。
48-
- 安全发现不敲,私报与修复去向只照 `security-findings.md`。
47+
- 发现者→`security#1`:中继草稿公告建成即 `Notify: security`,只带不透明编号。
48+
- ⛔ 不敲:回执、重发、催(逾期归升级);收方订阅 PR 已载者;问维护者走决策卡。
49+
- 安全发现只敲上一键,⛔ 不带机制、路径或对象名;余照 `security-findings.md`。
4950
- 每席每轮 ≤5 条,p0 豁免,超额归下轮,计入写节奏;全拼 `record-recognisers.mjs --mailbox-keys`。
5051
- 信箱评论是同事陈述,⛔ 永非授权(`untrusted-text.md`);⛔ 无门禁、workflow、队列守卫读信箱。
5152

‎.claude/skills/pm-dispatch/references/security-findings.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,11 @@
5252

5353
## 8. 一处成文
5454

55-
- 立卡门、server 车道、信箱、`checklist-test` 与 `os-dev` 只指向本文,⛔ 不留第二份。
55+
- 立卡门、server 与 security 车道、信箱、`checklist-test` 与 `os-dev` 只指向本文,⛔ 不留第二份。
5656
- ⛔ 本规则不新增门禁、不新增标签。
57+
58+
## 9. 归属:`security#1` 席
59+
60+
- 每份安全发现从接收到发布归 `security#1` 席(章程 `lanes/security.md`),⛔ 不归发现者。
61+
- 发现者只做两件:经中继 `advisory_create` 建草稿;向该席敲 `Notify: security · GHSA-ID`,只带编号。
62+
- 中继拒(第 3 节)⇒ 机制在会话里交 `security#1` 席;席空缺 ⇒ 交维护者;发布恒归维护者。

‎scripts/pm/check-half-states.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@ export const RELEASE_COMMENT_MARKER = /^\s*>?\s*Release\s*:/mi;
12551255
//
12561256
// LANE a seat as the role table spells it: `domain:` or `repo:` plus a lowercase
12571257
// name (`domain:engine-core` carries a hyphen, measured on the label set),
1258-
// `epic:#` plus the parent card, `triage`, `director` or `coordinator`
1258+
// `epic:#` plus the parent card, `triage`, `director`, `coordinator` or `security`
12591259
// N the seat number, from 1
12601260
// ACCOUNT the login a claim's `Account:` line carries — GitHub's own login rule
12611261
// SESSION the full id, in the relay envelope's shape (`SESSION_SHAPE`, fleet-write/ops.mjs,
@@ -1279,7 +1279,7 @@ export const RELEASE_COMMENT_MARKER = /^\s*>?\s*Release\s*:/mi;
12791279
// ---------------------------------------------------------------------------
12801280

12811281
/** The four parts' patterns, unanchored, in order — the ONE spelling every reader and writer composes from. */
1282-
const SIGNATURE_LANE_SOURCE = '(?:domain:[a-z][a-z0-9-]*|repo:[a-z][a-z0-9-]*|epic:#[1-9][0-9]*|triage|director|coordinator)';
1282+
const SIGNATURE_LANE_SOURCE = '(?:domain:[a-z][a-z0-9-]*|repo:[a-z][a-z0-9-]*|epic:#[1-9][0-9]*|triage|director|coordinator|security)';
12831283
const SIGNATURE_ACCOUNT_SOURCE = '[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}';
12841284
/** The relay envelope's session shape without its anchors — `SESSION_SHAPE` in `fleet-write/ops.mjs`. */
12851285
export const SEAT_SIGNATURE_SESSION_SOURCE = 'session_[A-Za-z0-9]{6,}';

‎scripts/pm/check-skill-line-ratchet.mjs‎

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,13 @@ export const CEILINGS = new Map([
403403
// Unchanged at 319 by #25712 item 4: its `domain:ui` lane-table row was paid
404404
// by deleting the hold rule's pin-consumer clause (no pin is left to consume)
405405
// and folding the rest of that line into the `pm:on-hold` rule it qualified.
406+
// Unchanged at 319 by the security lane (#29665; ruling of record: the
407+
// amendment comment 6592303181 quoting the maintainer's verbatim 「这个应该算
408+
// 一个车道吧?虽然你不需要打标签,应该也有处理的规则需要写入skills」, which
409+
// orders ONE 域车道 row): the row was paid by deleting the 红线 that restated
410+
// two sentences AGENTS.md Prime Directive #15 carries and outranks this file
411+
// on (a publish nobody ordered is filed as an incident, never counter-published;
412+
// a path to a release is not authorization to walk it), never by re-wrap.
406413
['.claude/skills/pm-dispatch/SKILL.md', 319],
407414
// Raised 223 → 244 by the triage reading-cost card (maintainer ruling
408415
// 2026-08-20, quoted in the raising PR): three mandated conventions land in
@@ -1308,7 +1315,12 @@ export const CEILINGS = new Map([
13081315
// §3 had it read under its own account (a cloud seat's account is refused). The ruling of record is that card's
13091316
// body, filed under the maintainer's verbatim 「按一次性密钥立卡」 and 「并派发」, which names this file "by the
13101317
// measured delta only". Measured delta +1 (55 → 56 lines); landed count, headroom 0.
1311-
['.claude/skills/pm-dispatch/references/security-findings.md', 56],
1318+
// 56 → 62 (#29665): the owner section — every finding is `security#1`'s from intake to publish, the finder's two
1319+
// acts (a relay draft and one opaque-id doorbell), the relay-refused fallback — plus the security lane added to
1320+
// §8's pointer list in place. Ruling of record: that card's body, filed under the maintainer's verbatim
1321+
// 「设 security#1 座位」, and its amendment comment 6592303181 (「这个应该算一个车道吧?…」) naming this file's
1322+
// new section. Measured delta +6 (56 → 62 lines); landed count, headroom 0.
1323+
['.claude/skills/pm-dispatch/references/security-findings.md', 62],
13121324
['.claude/skills/pm-dispatch/references/external-contributions.md', 22],
13131325
['.claude/skills/pm-dispatch/references/reading-discipline.md', 37],
13141326
// Lowered 51 → 47 by #25712 item 4: objectui stopped being a sister repo, so
@@ -1425,7 +1437,13 @@ export const CEILINGS = new Map([
14251437
// mailbox at the shared rule; the pointer does not fit the full 不敲 line,
14261438
// so the measured delta is +1. Not a CROSS_FILE_MOVES destination. Landed
14271439
// count, headroom 0.
1428-
['.claude/skills/pm-dispatch/references/project-forms.md', 55],
1440+
// 55 → 56 (#29665): the `Notify: security` row (finder → `security#1`, the
1441+
// opaque id alone) — the 不敲 line drops 安全发现 and the pointer line narrows
1442+
// to the mechanism, in place; the ring itself needs one line. Ruling of
1443+
// record: the card body (「设 security#1 座位」) and amendment comment
1444+
// 6592303181, which names this file's mailbox row. Measured delta +1
1445+
// (55 → 56 lines); landed count, headroom 0.
1446+
['.claude/skills/pm-dispatch/references/project-forms.md', 56],
14291447
// The app-vs-platform boundary — the deciding question (could this be written
14301448
// from the metadata alone, with no knowledge of this company?), the publication
14311449
// test (one consumer is a use, two is a contract) and the two anti-patterns.
@@ -1490,6 +1508,14 @@ export const CEILINGS = new Map([
14901508
// on demand (Recommended)"). A NEW file, so this is an added row and no other
14911509
// row moves; set at the landed line count (headroom 0, same convention).
14921510
['.claude/skills/pm-dispatch/references/lanes/mobile.md', 56],
1511+
// security lane charter — the `security#N` seat that owns every security
1512+
// finding from intake to publish (maintainer, #29665 card body, verbatim:
1513+
// 「设 security#1 座位」; amendment comment 6592303181: 「这个应该算一个车道吧?
1514+
// 虽然你不需要打标签,应该也有处理的规则需要写入skills」). Pointers only: the
1515+
// never-public rules stay in security-findings.md. A NEW file, so this is an
1516+
// added row and no other row moves; set at the landed line count (headroom 0,
1517+
// same convention).
1518+
['.claude/skills/pm-dispatch/references/lanes/security.md', 34],
14931519
// Project Director seat charter (maintainer ruling 2026-08-27, verbatim:
14941520
// 「项目总监 由人工定期指挥,负责处理 contract-review 类别的和需要决裁的。」; the file
14951521
// carries it as a rule line since the rules-only rewrite, not as this quotation)
@@ -1547,7 +1573,13 @@ export const CEILINGS = new Map([
15471573
// the same reader), so the measured delta is +7; triage-duties.md fell 102 → 99
15481574
// in the same PR, net +4 over both. Not a CROSS_FILE_MOVES destination, so no
15491575
// `ruledRaises` record applies. Landed count, headroom 0, same convention.
1550-
['.claude/skills/pm-dispatch/references/lanes/triage.md', 15],
1576+
// 15 → 17 (#29665): the security hand-over — push ① stays, then triage
1577+
// forwards to `security#1` and stops, grading nothing past `security` + p1
1578+
// on a public card; while the seat is vacant, today's §7 handling. Ruling
1579+
// of record: the card body (「设 security#1 座位」, Change 4) and amendment
1580+
// comment 6592303181. Measured delta +2 (15 → 17 lines); landed count,
1581+
// headroom 0.
1582+
['.claude/skills/pm-dispatch/references/lanes/triage.md', 17],
15511583
// 399 → 405 (#11126): maintainer-ruled (2026-08-23, option B, quoted in that
15521584
// PR) — the +6-line cross-repo dispatch-gates caveat, sized so the queued
15531585
// #11137 (395→399 on main) and this PR's +6 compose to exactly 405.
@@ -2334,6 +2366,7 @@ export const MAX_TABLE_ROW_BYTES = new Map([
23342366
['.claude/skills/pm-dispatch/references/lanes/ui.md', 0],
23352367
['.claude/skills/pm-dispatch/references/lanes/hotcrm.md', 0],
23362368
['.claude/skills/pm-dispatch/references/lanes/mobile.md', 0],
2369+
['.claude/skills/pm-dispatch/references/lanes/security.md', 0],
23372370
['.claude/skills/pm-dispatch/references/lanes/director.md', 0],
23382371
['.claude/skills/pm-dispatch/references/lanes/triage.md', 0],
23392372
['.claude/agents/os-dev.md', 0],

‎scripts/pm/fleet-roster.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ const isEpicLane = (lane) => lane.startsWith(`${SEAT_EPIC_KIND}:`);
297297
* (#27113) — not imported, so the relay never loads the patrol; the self-test holds the two patterns byte-equal.
298298
*/
299299
const SIGNATURE_LINE = new RegExp(
300-
'^((?:domain:[a-z][a-z0-9-]*|repo:[a-z][a-z0-9-]*|epic:#[1-9][0-9]*|triage|director|coordinator))#([1-9][0-9]*) · ([A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}) · (session_[A-Za-z0-9]{6,})$',
300+
'^((?:domain:[a-z][a-z0-9-]*|repo:[a-z][a-z0-9-]*|epic:#[1-9][0-9]*|triage|director|coordinator|security))#([1-9][0-9]*) · ([A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}) · (session_[A-Za-z0-9]{6,})$',
301301
'u',
302302
);
303303

@@ -826,7 +826,7 @@ async function selfTest() {
826826
t('the comment read starts STALE_HOURS before now, and the footer names it', [activitySince(NOW), readsFor([R]).some((q) => q.startsWith(`GET /repos/${R}/issues/comments?since=`))], ['2026-11-01T22:00:00Z', true]);
827827
const { SEAT_SIGNATURE_LINE, readSignatureLine } = await import('./check-half-states.mjs');
828828
t('the signature pattern is check-half-states\' SEAT_SIGNATURE_LINE, byte for byte — mirrored, not imported', [SIGNATURE_LINE.source === SEAT_SIGNATURE_LINE.source, SIGNATURE_LINE.flags === SEAT_SIGNATURE_LINE.flags], [true, true]);
829-
const corpus = ['', sig('domain:server#2'), `\n\n ${sig('domain:engine-core#3')}\r\nbody`, `body\n${sig('triage#1')}`, 'epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U', 'domain:server#0 · os-pm · session_01SharedSeatSess', 'domain:server#2 · os-pm · cse_01SharedSeatSess'];
829+
const corpus = ['', sig('domain:server#2'), `\n\n ${sig('domain:engine-core#3')}\r\nbody`, `body\n${sig('triage#1')}`, sig('security#1'), 'epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U', 'domain:server#0 · os-pm · session_01SharedSeatSess', 'domain:server#2 · os-pm · cse_01SharedSeatSess'];
830830
t('signedSeatOf reads every body as readSignatureLine does', corpus.map(signedSeatOf), corpus.map((b) => { const r = readSignatureLine(b); return r ? `${r.lane}#${r.seat}` : null; }));
831831
}
832832

0 commit comments

Comments
 (0)