Skip to content

docs(pm): the security lane charter carries the maintainer's working rules — reviewed escalation, cloud orders, close-out, intake - #29840

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-29726-security-charter-rules
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-29726-security-charter-rules

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #29726

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:安全席章程 lanes/security.md 写入你在本会话定的工作规则——呈你之前先经隔离复核、复杂修复开云卡、逐对象收口、修复合入即关公告、发版前呈发布与否清单、接收与读公告的规矩;误公开卡的转交读得懂了。
  • 为什么改:你说「这些都应该写到你的skills」,补充评论 6595626876 又把公告收口改为合入即关、发布在发版前定;同时收掉 PR docs(pm-dispatch): the security lane — a security#1 seat owns every security finding; its charter, the Notify: security doorbell and the seat signature #29695 复核的三条 should-fix。
  • 风险与回滚:只动席位规则文本与 scripts/pm 工具,不进任何发布包;回滚即 revert 本 PR。唯一行为变化是信箱门铃 Notify: security 多认一种「卡号」写法。
  • 先看:① .claude/skills/pm-dispatch/references/lanes/security.md(新增「呈维护者」「收口」两节);② scripts/pm/record-recognisers.mjs 的 notify:security 读法;③ scripts/pm/fleet-roster.mjs 签名行改为导入。
  • 两分钟验证:pnpm check:pm-record-recognisers,预期四个自测全绿(record-recognisers 360 例)。
  • 需要你做的:无,席位落地(Tier S,待 CONTRACT_REVIEW_TIER 复核 PASS)。

What changes

lanes/security.md (34 → 53 lines) — the maintainer's working rules for the security seat, as pointers plus the minimum new text; no never-public rule is restated:

  • Ruling 1 (verbatim on the card): before any question reaches the maintainer, an isolated agent at CONTRACT_REVIEW_TIER reviews the analysis — fed the analysis and evidence, never the seat's conclusion — checking the four axes, missing options, the recommendation and the evidence; the question carries the verdict and every dissent. No model name in the text.
  • Ruling 2: L/XL, build-heavy, long-running or browser-verified fix orders go mode:cloud, the rest mode:subagent, pointing at execution-duties.md; the seat archives each cloud card when its work lands.
  • Ruling 3, the close-out per object, as amended by comment 6595626876: PR merged by the queue, MERGED confirmed before the card's close-out (pointer to landing-operations.md); Fixes #N / Part of #N cards; an advisory moves to closed through the relay advisory_state once its fix merges, recorded on the seat post, no per-advisory ask; cloud cards archived; a subagent ends itself and removes its worktree.
  • Ruling 4 (amendment): a pre-release publish-or-not list of the advisories closed since the last release, high and moderate first, reviewed under ruling 1 first; the maintainer reopens and publishes in the advisory UI. Ledger states: received → fixing → merged → closed, and only what the pick sends to publishing goes awaiting publish → published.
  • Intake and reads: a cloud seat cannot list advisories (ids come from the maintainer or the doorbell); plaintext only to the seat's own stdout with --reveal, never a file, a log or a background task's output; fix claims live in the seat post's ledger.
  • S1: the old line 12 and line 17's clauses restated security-findings.md §3/§9; both are now pointers.
  • S2: one sentence — the seat's own patrol reads open security-labelled cards, so the label is the hand-over; triage's doorbell only speeds it up.

Notify: security reads a labelled card's number (S2's measured half). Triage already rang Notify: security · #29761 into the security mailbox (#29743, comment 6595011108). Measured on origin/main at b0ef25f: readMailboxLine answered unreadable ("does not spell Notify: security · GHSA-ID"), as the PR #29695 review predicted. Of the two fixes, widening the reader is the smaller change (no new key, no new table row, no new protocol line): the one field is either the opaque advisory id or a card number, never both. An already-public card number is not a mechanism. After the change the same comment reads { card: 29761, numbers: [29761] }. Template now Notify: security · GHSA-ID or #n.

S3. The MAILBOX_KEYS notify:security from: is now the seat holding the finding (a dev's or a QA runner's finding reaches its PM session privately first), or triage for a public card it labelled. project-forms.md says the same in place (still 56 lines, ≤120 bytes).

Lane-list three-copy drift. fleet-roster.mjs now imports SEAT_SIGNATURE_LINE from check-half-states.mjs and deletes its mirror. The mirror's stated reason ("so the relay never loads the patrol") is false on this tree: fleet-write/dispatch.mjs, the relay that imports ROSTER_MARKER, statically imports check-half-states.mjs itself (line 482). So the import loads nothing new there and adds no cycle, because check-half-states.mjs never imports fleet-roster.mjs. The third copy, mailbox-open.mjs SEAT_BARE_LANES, cannot import the patrol: the patrol loads the opener lazily, and H72 pins that, so the edge would be the top-level-await deadlock measured at check-half-states.mjs:14681. It is pinned instead by a new fleet-roster self-test row (the only file that imports both). The signature's kinds must equal the opener's kinds plus epic, and its bare words the opener's bare lanes plus coordinator. No new gate.

Ratchet. lanes/security.md row 34 → 53, the measured delta +19. The row's comment cites the card body and amendment comment 6595626876 as the ruling record. No other row moves.

Tests

  • node scripts/pm/record-recognisers.mjs --self-test → ✓ record-recognisers self-test: 360 cases pass (was 359; the mailbox battery floor 40 → 41). The old refusal row dropped its #28001 case. The new row reads #29761 alone and refuses a card plus an id, a second field, PR #n, #0 and the verbatim template.
  • node scripts/pm/fleet-roster.mjs --self-test → ✓ fleet-roster self-test: 105 cases in 9 batteries pass (was 104; battery floor 13 → 14).
  • node scripts/pm/mailbox-open.mjs --self-test → ✓ mailbox-open self-test: 82 cases in 10 batteries pass.
Ablations (one-time proofs, run through scripts/ablation-replace.mjs against the committed head 08f26b8)
  • Deleted the card-number branch of the notify:security reader: ✗ record-recognisers self-test: 1 of 360 case(s) failed (the new row), restored: blob == HEAD, git diff HEAD empty.
  • Added a bare lane auditor to SIGNATURE_LANE_SOURCE alone: the fleet-roster lane-grammar row reds (1 failure(s)), restored and proven.
  • Added auditor to SEAT_BARE_LANES alone: the same row reds, restored and proven.
  • Direction expected and observed: red, all three.

Acceptance notes

  • fleet-roster.mjs's isEpicParent header carries the same stale reason ("not imported, so the relay … never loads the patrol") for its H45 mirror. Untouched here; a note only.
  • Line 61 of security-findings.md §9 still says "发现者 … 敲 Notify: security · GHSA-ID". It still reads true: §5 sends a dev's finding to its seat first. It is unchanged because the claim limits that file to pointers.
  • SKILL.md's mode:* row routes security-labelled cards to thread when a seat is a project thread. The charter's cloud rule covers the seat's no-card fix orders, a different object. No change.

Verification

Gates derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 08f26b8 (38 commands). Every one ran, with the exit code captured before any pipe. Reconciled: ✓ dispatch-gates --ran: 38 derived famil(ies) accounted for — 38 run, 0 NOT-MEASURED (a DERIVED zero — all 38 recorded an exit code and none of them is 3).

Per-command exits

All exit 0, including pnpm check:pm-skill-ratchet, pnpm check:pm-skill-id-lint (109 file(s) clean), pnpm check:skill-frame-sync, pnpm check:pm-record-recognisers, pnpm check:pm-dispatch-gates, pnpm check:doc-authoring and pnpm check:nul-bytes. One exception: pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 (PREREQUISITE NOT MET: @objectstack/formula and @objectstack/lint unbuilt). It exited 0 after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the verify lock. Also run outside the derivation: the --self-test of each touched scripts/pm file (check-skill-line-ratchet, fleet-roster, mailbox-open, record-recognisers), all exit 0.

No changeset: .claude/** and scripts/pm/** ship in no released package (skip-changeset).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj


Generated by Claude Code

…rules

Reviewed escalation at CONTRACT_REVIEW_TIER before any question reaches the
maintainer, cloud orders for heavy fixes, the per-object close-out, the relay
closed move on merge, the pre-release publish-or-not list and the intake and
read rules; S1-S3 of the PR 29695 contract review. The Notify: security
doorbell reads a labelled public card's number alone; fleet-roster imports the
seat signature line instead of mirroring it, and pins the lane grammar.

Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 3, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Nov 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

Contract-review-claim: session_01QSeKYn5CmmdShCJifm6WYj · head 08f26b8 · 2026-11-03T06:58Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 08f26b8bb4b1ec4dff3e4e7c74d488a30583ead8
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/lanes/security.md, .claude/skills/pm-dispatch/references/project-forms.md + scripts/pm/record-recognisers.mjs, scripts/pm/fleet-roster.mjs, scripts/pm/mailbox-open.mjs, scripts/pm/check-skill-line-ratchet.mjs, scripts/pm/check-half-states.mjs, scripts/pm/fleet-write/dispatch.mjs, .claude/skills/pm-dispatch/references/security-findings.md, .claude/skills/pm-dispatch/SKILL.md, .claude/skills/pm-dispatch/references/execution-duties.md, .claude/skills/pm-dispatch/references/landing-operations.md, .claude/skills/pm-dispatch/references/seat-lifecycle.md, .claude/skills/pm-dispatch/references/lanes/triage.md

Inputs: card #29726 (body; comments 6595030876, 6595626876, 6596935692, 6597456457 — the last is the author's account and was tested, not cited), PR #29840 (body, file list, net diff against origin/main at 64646ae: 6 files, +66/−27), the head's check-runs. The tree was judged, never the PR body's account of it.

① Derived judgments

Each rule the charter now states, against the verbatim rulings on the card and in amendment 6595626876:

  • Ruling 1 「后续需要我决裁的问题,先用 fable 审核之后再和我沟通。」 → lines 32–34: any question to the maintainer is first reviewed by an isolated agent at CONTRACT_REVIEW_TIER; the reviewer takes the analysis and evidence and never the seat's conclusion; it checks the four axes, no missing option, the recommendation and that the evidence is real; the question carries the verdict and every dissent. Faithful; nothing widened or narrowed. The text writes the tier constant and 「⛔ 不写模型名」 — no model id anywhere in the diff (grepped).
  • Ruling 2 「复杂的任务可以开云卡片」 → line 26: L/XL, build-heavy, long-running or browser-verified fix orders go mode:cloud, the rest mode:subagent, pointing at ../execution-duties.md (its lines 147–152 carry the one-order-per-container and archive duties); the archive is restated on line 43. Faithful.
  • Ruling 3 「这些都应该写到你的skills」, the per-object close-out → lines 40–43. PR: the queue merges, MERGED confirmed before the card's close-out, pointer to ../landing-operations.md (its line 63 closes the Fixes/Part of cards in the same act). Card: Fixes #N closes on merge, the seat posts the landing record and removes pm:*; Part of #N the seat closes when its last part lands. Advisory: as AMENDED by 6595626876 item 1 (「目前的修复都关闭」「你处理完的可以都先关闭」) — the relay advisory_state move to closed on merge, recorded on the seat post, 「⛔ 不逐份请示」; the card's superseded "only when the maintainer names that advisory" line is correctly absent, and the old charter line 25's "pushes to awaiting publish and stops" is correctly gone. Cloud card archived by the seat; a subagent ends itself and removes its own worktree. Faithful.
  • Amendment item 2 「哪些高危要发布应该在我版本发布前决策吧」 → lines 35–36: before each release, a publish-or-not list of the advisories closed since the last release, high and moderate first, reviewed under ruling 1 first; publishing stays the maintainer's, who reopens and publishes in the advisory UI because the relay cannot move a closed advisory. Faithful. Item 3 (batch 5) is a seat-post setting and is correctly not in the charter.
  • Ledger state words → line 21: received → fixing → merged → closed; awaiting publish → published only for what the pre-release pick sends to publishing. Exactly the amendment's wording. The seat post [PM seat] security — 🟢 os-justin #29740 ledger already uses these words; no script pins the vocabulary (grepped scripts/ and .claude/: the charter is the one holder).
  • Intake and reads → lines 18–20: a cloud seat cannot list the repository's advisories and the relay refuses the bare collection, so ids come from the maintainer or the doorbell; plaintext is printed only to the seat's own stdout with --reveal, never into a file, a log or a background task's output; fix claims live in the seat post's ledger. The relay-read and sender-gate halves stay as the pointer on line 12. Faithful.
  • S1 resolved: old line 12 (relay read/write only) and old line 17's 「⛔ 不换账号重发」 restated security-findings.md §3 (its lines 22–25); both are pointers now (lines 12, 17 → §3, §9). S2 resolved by one sentence (line 48) plus the reader widening (③). S3 resolved: the MAILBOX_KEYS from: and project-forms.md line 47 name the seat holding the finding (a dev's or runner's finding reaches its PM session first, §5), or triage for a card it labelled.
  • §8 「一处成文」: no never-public rule from security-findings.md is newly restated. Lines 25 and 47 restate §3 line 28 and §7, but both predate this PR (on main). Should-fix, not a must: line 36 carries 「发布归维护者」, which §3 line 24 and §9 line 62 already state — a carry-over from old line 25; the new half of the line (reopen-and-publish in the UI) is the minimum new text and is right.
  • Names no finding, advisory id, path or mechanism: checked line by line. #N on line 48 is a placeholder; security-note.md, execution-duties.md and landing-operations.md are skill references, not code paths. The seat post's ledger ids are not repeated in the tree.

② Semver level

skip-changeset (label present; Check Changeset green): .claude/** and scripts/pm/** ship in no released package. PR body carries Clause-②: no and Metadata-protocol: none; no (narrowing) arm. Correct.

③ Boundary flags

  • notify:security reader widened to accept #n. The field is the one slot after the key; an opaque public-card number names no mechanism (the card is already public, and the mailbox PR is public either way), and the reader refuses a card beside an id, a second field, PR #n, #0 and the verbatim template — read in the new reader (NUMBER_REF is anchored ^#[1-9][0-9]*$) and pinned by the new self-test row. Template Notify: security · GHSA-ID or #n: sent verbatim it splits into two parts, the second matches neither shape, so the "a template is not an event" pin still holds. mailboxEvents/H72 only count unread rows, so the new card/numbers fields change no sweep. The measured half is real: triage's ring on the seat's mailbox (comment 6595011108) is exactly To: security#1 / Notify: security · #29761, which the main reader cannot read. Safe; the smaller fix was the right one (no new key, no new gate, no new label — card's 「⛔ No new gate」 and §8's 「不新增门禁、不新增标签」 held).
  • fleet-roster importing check-half-states. Verified on the head: fleet-write/dispatch.mjs line 482 statically imports check-half-states.mjs (line 484 imports ROSTER_MARKER from fleet-roster.mjs), so the deleted mirror's "the relay never loads the patrol" reason was already false — the claim is true. No cycle: check-half-states.mjs imports only invoked-as.mjs and seat-record.mjs statically and record-recognisers.mjs / mailbox-open.mjs lazily inside a function; nothing in that closure imports fleet-roster.mjs. No load-order risk: the only two importers of fleet-roster.mjs (dispatch.mjs, check-owed-reviews.mjs) already import check-half-states.mjs, and its process.exit paths sit under the isEntrypoint guard. SEAT_SIGNATURE_LINE carries flag u only, so the shared regex object is stateless under exec. The old SIGNATURE_LINE has no leftover reference.
  • mailbox-open self-test holds the lane grammars equal, coordinator aside. The pin lives in fleet-roster's self-test (the one file importing both), not in mailbox-open. It parses the alternation out of SEAT_SIGNATURE_LINE.source and requires kinds = SEAT_LANE_KINDS + epic and bare words = SEAT_BARE_LANES + coordinator, with the total length pinned (7 on this tree). A lane added to either side alone reds it. mailbox-open.mjs itself changes only a comment, and the stated reason it cannot import the patrol (lazy load from the patrol; H72 pins that) matches check-half-states.mjs line 28287 and its measured top-level-await note. The coordinator exception is stated in both files and is the card's own carve-out.
  • Ratchet row +19. lanes/security.md is 53 lines on the head (34 on main), every line ≤ 120 bytes; the row moves 34 → 53, headroom 0, no other row moves. The comment cites the card body (rulings 1–3 verbatim there) and amendment comment 6595626876 as the ruling record, which is what the card's acceptance asks; the file's other rows cite cards the same way.
  • Acceptance-notes items. (a) fleet-roster.mjs line 275's isEpicParent header repeats the stale "relay never loads the patrol" reason — true on the head, a comment only, correctly left. (b) security-findings.md §9 line 61 still says the finder rings Notify: security · GHSA-ID — still true for a finder's draft; the #n form is triage's, and the claim limits that file to pointers. (c) SKILL.md line 61 routes security-labelled CARDS to thread when the seat is a project thread; line 26 governs the seat's no-card fix orders. Different objects, no conflict.

④ Design judgment

  • lanes/security.md holds against the cited code and the rulings. Each pointer resolves on the head: ../execution-duties.md lines 147–152, ../landing-operations.md lines 45–73, security-findings.md §3/§6/§7/§9, triage.md line 16–17. Close-on-merge (line 42) does not conflict with §3 (line 24 reserves publishing, not closing) or §7 (line 51 reserves closing the public ISSUE, which line 47 still reserves; the relay closed move is on the advisory). Ruling 1 is free of model ids. Trying to break it: the one soft spot is line 48 — 「本席巡查自读 open 的 security 标签卡,标签即转交;分诊敲 … 只为提速」. Seat post [PM seat] security — 🟢 os-justin #29740 records Patrol: none (an on-demand seat, the exception seat-lifecycle.md line 86 allows), so for the seat as currently held the doorbell is the instrument that reaches it, not a speed-up. The rule is still right at the charter level (line 39 of seat-lifecycle.md makes a patrol part of seating; present-tense seat state lives on the seat post, not here), and the reader widening makes the hand-over work either way. Should-fix: spell the no-patrol case (「无巡查时门铃即转交」) in the next edit of this file.
  • project-forms.md line 47: the 〈信箱 PR〉 table pin in record-recognisers (every key named, no other) still holds — the key is unchanged. Nit: it attributes the labelled-card ring to 「持发现的席」 while the MAILBOX_KEYS row says triage; the two are the same actor for a labelled card and the line stays ≤ 120 bytes, so no change asked.
  • Gates on the head (their conclusions are the verdicts): Lint & Repo Gates, Lint · repo & tooling gates (carries check:pm-skill-ratchet, check:pm-skill-id-lint, check:pm-record-recognisers — the four self-tests), Lint · PM dispatch-gates, Lint · docs & code guards, Lint · CI, release & engine gates, Governed Surface Queue Guard, TypeScript Type Check, Check Changeset, Check PR Size, PR-body guards, Test UI, Test Core (2/8) — all success. Still running at write time: Test Core (1/8). Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI are path-skipped. No failure. The PR is draft on the base repository (no fork), no reviewers, no approving review — Tier S landing waits on this record and the last shard.

No must-fix. Should-fix (line 48's no-patrol case; line 36's carried-over 「发布归维护者」) and the line-47 nit are for the next edit of these files, not this PR.

Implemented-by: claude/issue-29726-security-charter-rules
Reviewed-by: session_01QSeKYn5CmmdShCJifm6WYj

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

席位核对

ACCEPT · head 08f26b8bb4b1ec4dff3e4e7c74d488a30583ead8 · 2026-11-03T07:13Z

  • Shape: 6 files, +66/−27. check-governed-merges.mjs --pr 29840 reports GOVERNED, Tier S (.claude/** only among the governed paths), so the seat lands it after an at-tier PASS. skip-changeset, Clause-②: no.
  • Contract review: comment 6597603005 records Served-tier: CONTRACT_REVIEW_TIER, a Head-sha equal to this head, and VERDICT PASS with no must-fix. Every rule in the charter was matched to the verbatim rulings on pm-dispatch(security): the security lane charter carries the maintainer's working rules — reviewed escalation, cloud cards, the close-out of every object, advisory intake and reads #29726's body and amendment 6595626876. No never-public rule is restated, and the text carries no finding, advisory id, path, mechanism or model id.
  • Lane-seat part (TIER_DEFAULT, the scripts/pm/** files), read against the diff: the notify:security reader also accepts a card number alone, with an anchored reference and a self-test row (triage's real ring on mailbox pm:mailbox — security#1 · os-justin #29743 now reads); fleet-roster imports SEAT_SIGNATURE_LINE instead of mirroring it (no cycle, and fleet-write/dispatch.mjs already loads check-half-states.mjs); a self-test holds SEAT_BARE_LANES equal to the signature lanes, with the coordinator exception named; the ratchet row rose by the measured +19, citing the card.
  • Check-runs at this head: 26 success, 20 skipped, 0 failed, all completed. The skips are path-filtered UI, build and dogfood jobs and opt-in harnesses, judged by name; check-expected-skips.mjs is NOT MEASURED (no node_modules in this container). mergeable_state is clean.
  • Out of scope: the review's should-fix (line 48 assumes a patrol, while the seat holds none, so for an on-demand seat the doorbell IS the hand-over) and its two nits go to the next charter edit. They do not block this PR.
  • Landing: ready, then auto-merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant