Skip to content

docs(os-dev): label step scope and precedence; maintainer digest owed on the rules layer only - #18898

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-18699-os-dev-label-scope
Sep 18, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-18699-os-dev-label-scope

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Fixes #18699
Fixes #18812
Fixes #18821
Clause-②: no

Family fold of three graded cards on ONE file, .claude/agents/os-dev.md (governed, rules layer, CONTRACT_REVIEW_TIER): the label-timing clause that had no scope (chain head), the unconditional 速读 clause against the tiered landing, and the label step against a dispatch order that forbids it. One commit per card, in that order. #18698 remains open and #18882 remains open — neither is addressed here.

What changed (three commits; anchored on content — the cards' line numbers were readings at older shas)

Commit 1 — the label band gets its scope, its exception and the empty-intersection action.

  • The timing line 「本仓库:标签是真实机制 … PR 一开出就打」 gains a scope line directly under it: 「范围 = 派发词点名的标签,needs:contract-review 除外;交集为空 ⇒ 零写,报告写明。」
  • The carve-out line 「needs:contract-review 归席位,⛔ 不挂不摘不等;报 PR 上有无与 --pair PR-NUMBER 退出码作读数。」 moves down UNCHANGED from the PR-body band into the label band, so scope, exception and the empty-intersection action are three consecutive lines — no new special-case sentence, which the triage names as the source of the contradiction.
  • Paid in-file: the size-labeler line is retired by folding — its cause 「size-labeler 整组 PUT」 into the read-back-is-necessary-not-sufficient line, its wrap-up read-back 「收尾再读一次」 and 「清单进报告」 into the read-back line, and its 「标签没了就重挂」 was already that line's 「重挂」. Bytes on the two folded lines came from 「并发」 and 「语义」 (the ratchet's 120-byte line budget; both were adjectives whose meaning the neighbouring lines still carry).

Commit 2 — the label step is a default the dispatch may narrow or forbid.

  • 「打标签是你的步骤、不是 CI 的」 becomes 「打标签是你的默认步骤」 and the line ends 「;派发词可收窄或禁写。」; the scope line's action becomes 「禁写或交集为空 ⇒ 零写,报告写明」.
  • Category, said once: the step is a DEFAULT, not an unconditional clause. The standing precedence line 「派发词与本文件冲突时以本文件为准:无条件条款住这里,错了也在这里改。」 is untouched and does not contradict this: it governs the unconditional clauses, and the label line now says of itself that it is not one — a dispatch that forbids the write is not in conflict with this file, it is exercising what this file grants.
  • The write-budget line 「写预算四笔 …」 is untouched: 「预算」 plus 「预算外零写」 already read as a ceiling, and the scope line now names the dispatch as the source of the label list, so the budget cannot be read as an entitlement to a label the dispatch did not name.
  • 「、不是 CI 的」 is dropped for bytes: 「你的默认步骤」 and 「PR 一开出就打」 already say nobody else applies it for you.

Commit 3 — the 速读 draft is owed on the rules layer only.

  • 「受管面(见 AGENTS.md)PR 正文带 ## 维护者速读(草稿) 节 …」 becomes two lines: the tiering 「受管路径全在 .claude/skills/pm-dispatch/references/ 者为事实层,席位复审即记录;余为规则层。」 then the per-layer rule 「规则层 PR 正文带 ## 维护者速读(草稿) 节,中文、业务角度,席位意见留空;事实层不欠。」 The predicate is the one AGENTS.md Prime Directive 14 and pm-dispatch SKILL.md 「受管面两层」 already use. 「(见 AGENTS.md)」 is dropped for bytes — the file's own preamble makes AGENTS.md binding.
  • 「PR 正文 dev 只写一次 … ⛔ 不 PATCH」 is untouched; the conflict closes by the definition tracking the landing, not by allowing a body patch.
  • Paid in-file: the first bullet of the wrap-up section, 「终报消息之前,把同一段 JSON 发成 issue 评论,首行单独一行、就是字面纯文本 os-dev-report。」, duplicated the preamble's sentence; its two extra bits fold into the preamble line, now 「报告交付两次,GitHub 优先:同一段 JSON 先作 issue 评论,首行单独一行是纯文本 os-dev-report。」 (「字面」 dropped for bytes; the next preamble sentence already refuses the HTML-comment spelling). This continues the dedup the previous os-dev.md PR started in the same spot.

Reader tests — a dev reading ONLY os-dev.md

  1. 「the only label I could hang is needs:contract-review — what do I do?」 → the scope line: it is 除外, so the intersection is empty ⇒ 零写,报告写明; the next line: report presence on the PR and the --pair exit code. Write nothing, say so.
  2. 「my PR's governed paths are all under .claude/skills/pm-dispatch/references/ — do I write the 速读 section?」 → the tiering line says that is the fact layer and the seat's review is the record; the rule line says 事实层不欠. No.
  3. 「my dispatch order forbids the label write — do I still do it?」 → the timing line: 派发词可收窄或禁写; the scope line: 禁写 ⇒ 零写,报告写明. No; the dispatch wins; the report names what is now the seat's to hang.

No answer needs a second text.

Line budget (check:pm-skill-ratchet, run at ee4c3c6f9)

.claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0) before and after; every line within the 120-byte budget. Added: the scope line, the tiering line (+2). Retired: the size-labeler line (folded, commit 1) and the wrap-up section's duplicate report-comment line (folded into the preamble, commit 3) (−2). Moved unchanged: the needs:contract-review carve-out line. Net 0 at each of the three commits, so the ratchet is green at every commit.

Verification

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at ee4c3c6f9 derived 19 commands (change set: 1 path vs merge base 600b1e2ab); all 19 run with redirect-then-capture, exit 0 each; --ran reconciliation: 「19 derived, 19 run, 0 NOT-MEASURED, 0 UNRUN」. Named families: check:pm-skill-ratchet (403/403 as above), check:pm-skill-id-lint (27 files clean), check:agent-model-declared (os-dev.md → opus), check:nul-bytes, check:skill-frame-sync, check:agent-test-spelling, check:doc-authoring, check:commit-card-trailers, check:pm-governed-merges, check:cross-package-test-inputs, check:watch-hint-literal, check:refd-timer-probe, check:driver-memory-census, the comment-mask corpus sweep, closing-keyword parity plus its self-test, the governed-queue-guard self-test, the harness-current self-test, and @objectstack/lint check:doc-formula-expressions (first run exit 3 = PREREQUISITE NOT MET on unbuilt formula/lint packages, so not a measurement; both rebuilt under the verify lock, rerun exit 0). Also run, outside the derived set because the derivation marks its roster as sitting under .claude: check:pm-settings-deny-roster, exit 0. The 14 pending-changeset families do not apply: this PR carries no changeset (skip-changeset — nothing under any package's files[] moves; the file is under .claude/**, the fast lane).

The pre-push trailer check passed on the push: three commits, no card relation, no model identifier in the trailer pair.

Acceptance notes

  • noted, not filed: the 「或 tail --pid 等它退出」 line in the wrap-up section names no pid source — a rider the previous seat left for the next os-dev.md PR; untouched here (this fold edits the section's first bullet, not that band). 承接者:the next entry on the os-dev.md serial.
  • noted, not filed: os-dev.md keeps ASCII sentence punctuation after Han characters throughout (the ratchet's own comment measures it); the new lines follow it. No gate pins the convention. 承接者:无.
  • The harness-injected attribution reminder for this session names a model in the co-author line and prescribes a different PR footer; AGENTS.md (which that reminder itself defers to) declares the trailer pair model-free and fixes the footer form, and check:commit-card-trailers refuses the model-named form at push — the commits and this body follow AGENTS.md. This is the shape of the card still in the decision box (#18698 remains open); not re-litigated here.

维护者速读(草稿)

改了什么:os-dev.md(开发 agent 的标准合同)三处子句改写,行数不变(403/403)。① 挂标签那条补齐范围、例外与交集为空时的动作;② 写明挂标签是默认步骤,派发词可收窄或禁;③ 「维护者速读」只在规则层受管面欠,事实层(references/ 目录)不欠,以席位复审为记录。

为什么改:三张卡量到同一个病:合同里的子句与回路实际跑的分层脱节,每次都靠现场的 dev 自行裁断。已量到的代价:一次升级轮加一次板上半写状态;两次事实层 PR 靠派发词手工豁免;一次云端卡上 dev 从零裁断先后关系。三位 dev 都做对了(声明冲突而不是静默选边),但「dev 推理出了正确答案」不是规则。

风险与代价(含回滚):纯文本改动,不发布、不动门禁、不动 frontmatter。风险在措辞:读者只读 os-dev.md 能否答出三个问题——正文 Reader tests 逐一作答。回滚 = revert 三个 commit 中任一或全部,每个 commit 各自净 0 行,互不依赖。

席位意见:(留空)

你要做的:审阅三条改写后的子句是否与你对分层落地的理解一致,一致即批准(需 GOVERNED_APPROVERS 账户的 APPROVED)。


Generated by Claude Code

…pty-intersection action

The label-timing clause stated an obligation with no scope, and the
`needs:contract-review` carve-out sat twelve lines above it in the PR-body
band; on a card where that carve-out was the only label owed, the literal
reading hung the clause-② gate on the dev's own PR. The label band now reads
as one block: scope = the labels the dispatch names, the seat's label excepted;
an empty intersection means write nothing and say so in the report. The
carve-out line moves down unchanged to sit inside the band it governs.

Paid in-file (403/403): the size-labeler line is folded — its cause into the
read-back-is-not-sufficient line, its wrap-up read-back and list-in-report into
the read-back line, and its re-hang instruction was already stated there.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
…forbid

The write budget and the label-timing clause read as an entitlement, while a
dispatch order from a repo whose label mechanism is the seat's forbids the dev
the write; each dev on such a card re-adjudicated the precedence from scratch.
The timing line now names the step a default and says the dispatch may narrow
or forbid it; the scope line says a forbidden write, like an empty
intersection, means write nothing and say so in the report. The standing
「派发词与本文件冲突时以本文件为准:无条件条款住这里」 clause is untouched: it
governs the unconditional clauses, and this step now says once that it is not
one. Bytes for the tail come from dropping 「、不是 CI 的」, which 「你的默认步骤」
plus 「PR 一开出就打」 already state.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
The 速读 clause was unconditional over the governed surface while the landing
is tiered: a PR whose governed paths all lie under the pm-dispatch references
directory lands through the queue on the seat's contract review, so no
maintainer reads its body and the five paragraphs had no reader; the body may
not be patched after creation, and every fact-layer dispatch waived the clause
by hand. The definition now tracks the standing tiering: the fact layer is
named by its path predicate with the seat's review as its record, and the
draft section is owed on the rules layer only.

Paid in-file (403/403): the report-comment sentence at the top of the
wrap-up section duplicated the preamble's; its two extra bits (the same JSON,
the marker alone on its first line) fold into the preamble line, and the byte
budget takes 「字面」 there — the next sentence already refuses the HTML-comment
spelling.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
…he file's own skip-changeset criterion

The scope line landed in this branch made the dispatch the only source of
the label list, while the block six lines above still makes `skip-changeset`
a label the dev decides by the file's own criterion; a dev whose dispatch
does not name it held two readings of one file — the shape this branch
treats. The scope now names both: the labels the dispatch names plus the
`skip-changeset` the criterion above yields. The `needs:contract-review`
carve-out leaves the scope line and is stated once, on the line directly
below it (「归席位,⛔ 不挂不摘不等」), which is where it already lived; the
forbidden/empty action is unchanged. 403/403, the line measures 118 bytes.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF

Copy link
Copy Markdown
Collaborator Author

CI reading on PR #18898 @ ee4c3c6f94 (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF, 2026-09-18T05:09Z): TypeScript Type Check reported failure after 4 seconds (05:07:46Z → 05:07:50Z, run 35308831944, job 105488666100). Step conclusions read from the job record: first failing step = Verify every type-check lane succeeded; steps: Set up job=success; Verify every type-check lane succeeded=failure; Complete job=success. The diff is one prose file (.claude/agents/os-dev.md, +8 / −8), the same check is success on the base tip 84ba4a847 (04:51Z) and on the merge-base 600b1e2ab (04:31Z), and the sibling PR #18903's type-check legs on bc0c2ec41e are success — the failure names nothing this diff touches and died before any type-check body ran. No manual re-run is spent: a patch commit for the seat's round-2 review is being pushed on this branch and carries a full set of runs on the new head; if the same step fails there in the same shape, that second failure is real and gets its own reading. The job log itself is not readable through this seat's proxy (CONNECT 403 to the log blob host) — the step record above is the reading.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Correction to the CI reading above — the aggregator failed because a LANE did not succeed

2026-09-18T05:10Z · the failing step of TypeScript Type Check is 「Verify every type-check lane succeeded」, the aggregator over the four Type Check · lanes, so 「died before any type-check body ran」 in my comment above is WRONG: a lane ran and did not succeed. Lane readings from the check-run and job records (this act):

  • Type Check · workspace on ee4c3c6f94: cancelled (2026-09-18T04:55:50Z -> 2026-09-18T05:07:43Z); first non-success step: Type check workspace packages (cancelled); steps: Set up job=success; Checkout repository=success; Setup Node.js=success; Setup pnpm=success; Get pnpm store directory=success; Setup pnpm cache=success; Restore Turbo cache=success; Install dependencies=success; Build workspace packages=success; CLI transcripts in content/docs match the registry they quote=success; Type check workspace packages=cancelled; Save Turbo cache (main only)=skipped; Post Setup pnpm cache=skipped; Post Setup Node.js=skipped; Post Checkout repository=success; Complete job=success; the same lane on the base tip 84ba4a847: [(None, None), ('success', '2026-09-18T04:51:11Z')].

Disposition unchanged: the patch commit for the seat's round-2 review is being pushed on this branch and carries a full set of runs; the seat reads the lane on the new head before any landing act, and a repeat of the same step in the same shape on a prose-only diff is judged against the base's own run of that lane, never re-run blind.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ea7d40f8e8c926e2c93d1351a81d7b0fe944def9

① Derived judgments

② Semver level

  • skip-changeset is correct: .claude/** publishes nothing (the file's own fast lane). Clause-②: no declared on the claim and in the body; PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18898 at 2026-09-18T05:14Z: exit 0, one live claim per thread, no widening tell.

③ Boundary flags

Implemented-by: claude/issue-18699-os-dev-label-scope
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

维护者速读(终稿)— PR #18898 · 席位定稿 2026-09-18T05:16Z(草稿在正文,以本评论为准)

改了什么:os-dev.md(开发 agent 的标准合同)三处子句改写,四个 commit,行数不变(403/403),每行不超 120 字节。① 挂标签这一步补齐范围、例外与交集为空时的动作,定稿三行:「打标签是你的默认步骤,PR 一开出就打;派发词可收窄或禁写」/「范围 = 派发词点名的标签 + 上文判据下的 skip-changeset;禁写或交集为空 ⇒ 零写并报告」/「needs:contract-review 归席位,⛔ 不挂不摘不等」;② 「维护者速读」只在规则层受管面欠,事实层(references/ 目录)不欠、以席位复审为记录。正文里 commit 1 那段引的是第一轮的旧句,以上面的定稿为准。

为什么改:三张卡量到同一个病——合同子句与回路实际跑的分层脱节,每次都靠现场 dev 自行裁断。已量到的代价:一次升级轮加一次板上半写状态(#18699)、两次事实层 PR 靠派发词手工豁免(#18812)、一次云端卡上 dev 从零裁断先后关系(#18821)。第一轮的范围句只认派发词,与四行之上的 skip-changeset 「唯一判据」又成了两种读法,第二轮改成两个来源都认。

风险与代价(含回滚):纯文本,不发布、不动门禁、不动 frontmatter;19 条派生门禁全绿。风险只在措辞:只读 os-dev.md 的 dev 能否答出三个问题(唯一可挂的标签是 needs:contract-review 怎么办 / 事实层 PR 要不要写速读 / 派发令禁写还挂不挂),席位按定稿逐一验过。回滚 = revert 任一或全部 commit,各自净 0 行。

席位意见:建议批准。三处都是把已经在跑的分层与先后关系写进合同,不新增机制;唯一取舍是为字节预算去掉了「(见 AGENTS.md)」指针,序言已说明 AGENTS.md 有约束力。

你要做的:审阅定稿三行 + 速读两行是否与你对分层落地的理解一致;一致即 APPROVE(GOVERNED_APPROVERS 账户),之后由本席按裁决 C 落地。


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — ruling C, on an authorized APPROVED (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T11:23Z

os-zhuang (GOVERNED_APPROVERS) approved this PR (review 5247206503 at 2026-09-18T11:22Z), marked it ready at 2026-09-18T11:21Z and added it to the merge queue in the same minute — the two CCR calls this seat's channel could not make, made by the approver's own hand. The seat's pre-landing reads on head ea7d40f8e8: the review of record 5725524949 names this head; PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18898 exit 0 on all three cards (#18699, #18812, #18821 — each Fixes); checks at 2026-09-18T11:22Z: 17 success · 13 skipped · 1 in progress (the queue's run), none red. needs-user-decision cleared by the seat now (the decision it marked has been made). The merge is the queue's; the three cards close on it, and the seat clears their pm:dispatched / assignee residue after the merge lands and lifts the os-dev.md serial (#18908 · #18882 · #19002).


Generated by Claude Code

This was referenced Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment