Skip to content

Commit e872ef4

Browse files
os-elon-muskclaude
andauthored
feat(pm): a PR over 5,000 changed lines lands only by a human merge — size predicate in check-governed-merges --test, the same reading in dispatch-gates, one rule line in SKILL.md and landing-operations (#19033)
Fixes #19012 Clause-②: no ## Maintainer ruling (verbatim, 2026-09-18) > 「还有应该完善skills,修改代码量超过某个行数(比如5000)就应该人工审核。」 Read as: a pull request whose changed line count — GitHub's `additions + deletions` on the PR, generated files INCLUDED — exceeds 5,000 lands only by a human merge, at the same terminal as governed text (ACCEPT on the card, `needs-user-decision` on the PR, a final 维护者速读, review requested from `GOVERNED_APPROVERS`); no seat flips it ready or arms auto-merge. 5,000 is the ruled default (「比如」), declared once as `HUMAN_MERGE_LINE_THRESHOLD` in `scripts/pm/check-governed-merges.mjs`, so it moves by one word from the maintainer and one edit. The case that prompted it, PR #18971 (+238,310 / −119, of which 237,706 lines were regenerated artefacts), is the first PR the rule governs — an exemption for generated files would exempt exactly it, so there is none. ## What changed 1. **`scripts/pm/check-governed-merges.mjs` — the SIZE predicate.** `--pr N` reads `additions` / `deletions` off the same `GET /repos/OWNER/REPO/pulls/N` that gives `changed_files` (a PR object missing the pair is a refusal on exit 1 — never a size of zero, never a "not governed" answer); `--branch REF` counts the same merge-base range with `git diff --numstat --no-renames` (a binary file is 0 lines, as GitHub counts it); `--test PATHS` takes `--additions N --deletions N` as a pair, or prints `size: NOT MEASURED` on stdout naming the modes that read it. Either limb exits on the GOVERNED code 3, so every caller that already routes 3 to the human terminal routes an oversized PR there without a new code; `--json` carries `size` and `humanMerge` (`governed` stays the path limb). A certified generated-artifact regeneration lifts the PATH off the register and lifts nothing from the size. The queue guard's `testVerdict(paths)` reading is unchanged (no size handed in ⇒ the path answer as before). 2. **`scripts/pm/dispatch-gates.mjs` — the same reading at dispatch time.** With no paths (the derived run) it prints `Changed lines — N (+a / -d; generated files INCLUDED) vs the human-merge threshold 5000: under` or `⛔ OVER — this PR lands only by a HUMAN MERGE …` beside the tier verdict (human and `--tier` modes), the count on stderr with the rest of the provenance, and `changedLines` in `--json`. The count is `--numstat` off the merge base against the working tree plus untracked files counted from disk (under-derivation refused, like the path list). An explicit path list carries no diff and prints `NOT MEASURED`, never a silent under. The threshold is imported from the gate — one declaration, no second copy. 3. **Rule text.** `.claude/skills/pm-dispatch/SKILL.md` gains one line beside the four-piece-terminal trigger (line 608, 111 B): 「改动 >5000 行(含生成物)同换终局四件套,⛔ 无事实层例外;读数 = PR additions+deletions。」 `references/landing-operations.md` line 26 folds the size limb into the pre-check row, now spelled `--pr N` (which reads paths and size in one call), 117 B, ceiling unchanged at 69. The SKILL.md ceiling rises 812 → 813 in `scripts/pm/check-skill-line-ratchet.mjs` under the ratchet's own maintainer exit, the ruling quoted in the entry (the 811 → 812 precedent's form). ## Readings — before / after, measured | reading | before (`43f476688`) | after (this head) | |---|---|---| | `check-governed-merges.mjs --pr 18971` (live API through the proxy) | exit 0 — `✅ NOT governed — ordinary queue landing applies` | exit 3 — `⛔ HUMAN MERGE — 238429 changed line(s) (+238310 / -119) > 5000` | | `--pr 18994` (2 files, +15 / −1) | exit 0 | exit 0 — `size: 16 changed line(s) (+15 / -1) ≤ 5000 — under the human-merge threshold` | | `--pr 18921` (SKILL.md, +6 / −6) | exit 3 GOVERNED | exit 3 GOVERNED, plus `size: 12 changed line(s) … under` | | `check-governed-merges.mjs --self-test` | 328 assertions, 26 batteries | 369 assertions, 27 batteries (new battery: the SIZE predicate, floor 30) | | `dispatch-gates.mjs --tier` (no paths, this worktree) | no size line | `Changed lines — 722 (+691 / -31; generated files INCLUDED) vs the human-merge threshold 5000: under.` | | `dispatch-gates.mjs --tier packages/spec/src/index.ts` | no size line | `Changed lines — NOT MEASURED: a path list carries no diff to count …` | | `check:pm-dispatch-gates` (detached, `tail --pid`) | 1849 cases (the dispatch's reading at `43f476688`) | 1862 cases pass (795.6 s, detached; +13 cases) | | `check:pm-skill-ratchet` | SKILL.md 812 / 812 · landing-operations.md 69 / 69 | SKILL.md 813 / 813 · landing-operations.md 69 / 69 | Self-test pins on the threshold: exactly 5,000 changed lines is under; 5,001 is over; the +238,310 / −119 pair reads 238,429 and is over; a certified pure regeneration over the threshold still lands by a human merge; the verdict is byte-identical through `--branch` and through `--test` once the same list and numbers are handed in. ## Line budget (measured) - `SKILL.md`: 812 → 813 lines; ceiling 812 → 813 (maintainer exit). A fold was not available: 0 of 598 adjacent bullet pairs merge under the 120-byte cap (smallest 123 B); the trigger line (607) stands at 118 B; the rule's shortest self-contained form is 111 B; deleting a ruled clause is refused on the state-machine precedent. - `references/landing-operations.md`: 69 → 69 lines (line 26: 118 B → 117 B). - `check:pm-skill-id-lint`: 27 files clean (no issue-ID citation in either line). ## Gates (this head; exit codes captured before any pipe) Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree at `7fdd61ca0` (42 commands; change set 5 paths, 724 changed lines by its own reading), every one run with `cmd > log 2>&1; status=$?` and reconciled with `--ran`: ```text node scripts/check-ci-filter-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs --self-test :: exit 0 node scripts/check-scripts-symbol-anchors.mjs :: exit 0 node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0 node scripts/check-self-test-wired.mjs :: exit 0 node scripts/check-self-test-wired.mjs --self-test :: exit 0 node scripts/check-self-test-workflow-commands.mjs :: exit 0 node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0 node scripts/check-skills-token-ratchet.mjs :: exit 0 node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0 node scripts/check-whole-set-label-write.mjs :: exit 0 node scripts/check-whole-set-label-write.mjs --self-test :: exit 0 node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0 node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0 node scripts/pm/check-harness-current.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:bash32-floor :: exit 0 pnpm check:cli-command-ids :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:declared-population-live :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:entry-guard :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:parse-guard :: exit 0 pnpm check:pm-expected-skips :: exit 0 pnpm check:pm-governed-prose :: exit 0 pnpm check:pm-half-states :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:pm-skill-ratchet :: exit 0 pnpm check:pnpm-filter-targets :: exit 0 pnpm check:ratchet-remedy-authority :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:skill-frame-sync :: exit 0 pnpm check:watch-hint-literal :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-dispatch-gates :: exit 0 ``` `dispatch-gates --ran`: **42 derived, 42 run, 0 NOT-MEASURED, 0 UNRUN** (verdict line: `✓ dispatch-gates --ran: 42 derived famil(ies) accounted for — 42 run, 0 NOT-MEASURED`). `check:pm-dispatch-gates` ran detached (`nohup` + `tail --pid`, 795.6 s on this box): `✓ dispatch-gates self-test: 1862 cases pass.` `check:pm-governed-merges`: `✓ check-governed-merges --self-test: 369 assertions`. `check:doc-formula-expressions` exited 3 (PREREQUISITE NOT MET: `@objectstack/formula` / `@objectstack/lint` not built) on the first pass; both were built under `scripts/pm/os-verify-lock.sh` (VERDICT command-exit 0, 152 s held) and the gate reran green — the exit 3 was never a measurement. NOT MEASURED locally, by the derivation itself (CI-only, value-bearing argv): `scripts/check-shard-attestation.mjs --emit …`, `scripts/check-test-completeness.mjs …`, `scripts/pm/check-half-states.mjs --format=markdown --provenance=…`; plus the 11 wide-population families and the 50 artifact-roster families CI runs on every PR, outside the derived total by design. `pnpm lint` (repo-wide eslint) is CI-owned and was not run here. No package build/test is owed: the diff touches no `packages/**` file (no ①/② in the local verification scope), so the only lock-wrapped run was the formula/lint build above. Line-budget after the final commit (`7fdd61ca0`): `check:pm-skill-ratchet` — `.claude/skills/pm-dispatch/SKILL.md is 813 lines (ceiling 813; headroom 0)`, `references/landing-operations.md is 69 lines (ceiling 69; headroom 0)`; `check:pm-skill-id-lint` — 27 file(s) clean. ## Deviations from the dispatch brief 1. Mechanism assumption 1 said a failing size read exits PREREQUISITE NOT MET (3). Under `--pr`, 3 already means GOVERNED — the file's own rule is that no invocation carries both meanings — so a PR object without the pair is a REFUSAL on the derivation code 1 (a stated refusal, never 0, never a size of zero). The ruling's intent (never read as "not governed") is kept. 2. "812 / 812 — fold or pay": measured, neither was available (above), so the SKILL.md line lands under the ratchet's own maintainer exit (812 → 813), the form the 811 → 812 entry took. The hunk sits at :608, disjoint from PR #18903's bands (:509–:525, :633–:675) and from the two PRs that landed on SKILL.md meanwhile (merged into this branch; the line is still there once). If the seat prefers the follow-up route, drop commit 3's SKILL.md hunk and the ratchet entry together. 3. `--branch` derives the size itself (`--numstat` on the range it lists) rather than taking passed-in numbers; the flags beside a deriving mode (`--pr`, `--branch`) are refused as two readings of one number, the way two mode flags are. 4. `dispatch-gates.mjs`'s self-test pins a NAMED census of live population markers by file and line; the import block moved this file's own `inherited-population` marker from :702 to :705, so that one row is updated — the census exists to be updated exactly this way. ## Acceptance notes - to file (class b — a declared contract the queue cannot yet hold): the queue guard's `merge_group` leg reads the PATH register only; a seat that skips the landing pre-check can still enqueue an oversized PR. Dedupe words: `queue guard size threshold`, `merge_group additions deletions`, `check-governed-queue-guard 5000`, `human merge line count`. - to file (class b): AGENTS.md §7 lists "two classes of PR never enter this path on green alone" (governed surface; Version Packages) — the ruled third class is missing from the rules layer. Dedupe words: `AGENTS.md green alone third class`, `5000 lines human merge AGENTS`. - noted, not filed: the post-merge sweep (default mode of `check-governed-merges.mjs`) lists governed-surface merges only; an oversized PR that landed through the queue is not listed. 承接者: the skills seat, together with the queue-guard follow-up above. - noted, not filed: `check:doc-formula-expressions` exits 3 (PREREQUISITE NOT MET) on a fresh worktree until `@objectstack/formula` and `@objectstack/lint` are built — by design of that gate; built under the verify lock here and rerun. 承接者: none. ## 维护者速读(草稿) **改了什么**:落地前检 `check-governed-merges.mjs` 新增「体量」判据:PR 的 additions + deletions 超过 5000 行(含生成物)⇒ 只能人合,与受管面走同一终点;`dispatch-gates` 在派发/认领时就把同一读数印在 tier 行旁;SKILL.md 与 landing-operations.md 各落一行规则。阈值只声明一次(`HUMAN_MERGE_LINE_THRESHOLD = 5000`),改它是一个词。 **为什么改**:您 2026-09-18 的裁决。触发案例是 PR #18971(+238,310 / −119,其中 237,706 行是生成物)只凭 AI 审查就经队列合入;生成物不豁免,否则恰好豁免它。 **风险与代价(含回滚)**:大 PR 的落地从「席位挂 auto-merge」变成「等您点一下」,每张超 5000 行的 PR 多一次人工动作;回滚 = revert 本 PR(纯脚本 + 两行规则文本,无发布物)。已知缺口:队列守卫的 merge_group 腿尚未读体量,眼下靠席位跑落地前检;已列为后续单。 **席位意见**:(留空) **你要做的**:确认 5000 这个默认值(「比如」)是否就是您要的;是 ⇒ 人合本 PR;要改数字 ⇒ 说一个数即可。 --- _Generated by [Claude Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6819dcb commit e872ef4

5 files changed

Lines changed: 694 additions & 33 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
605605
- governed 面统一定义:`docs/adr/**` + `.claude/**`(全量)+ `skills/**` + `docs/NORTH-STAR.md`。
606606
- governed 面同含 `AGENTS.md` + `CLAUDE.md`;`GOVERNED_REPOS` 各仓同治理待遇,执行席恒随落地仓车道。
607607
- 路径面命中规则层 ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。
608+
- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无事实层例外;读数 = PR additions+deletions。
608609
- ① 复核结论照常写在 issue 上;技能面 hunk 须由契约复审档的席复核,档外席先交 skills 席。
609610
- ② PR 留给维护者看得见地悬着;终局两条:人工直合即审核记录;授权批准 ⇒ 席位落地。
610611
- 看得见 = ACCEPT 同笔挂 `needs-user-decision` + 贴终稿「维护者速读」评论;①仍是审核记录。

‎.claude/skills/pm-dispatch/references/landing-operations.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
- 确认 MERGED 要两个读数:每轮同时读队列分支与 `origin/main`。
2424
- 契约复核 PASS 落地的 PR 到窗口时已 ready 且 auto-merge 在挂,见 `contract-review.md`。
2525
- 窗口自身权责不变:跟到 MERGED、踢出处置、落地后对账。
26-
- 转 ready 或挂 auto-merge 前先判受管面:`node scripts/pm/check-governed-merges.mjs --test` 加变更路径。
26+
- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000 行(含生成物)照规则层。
2727
- 受管路径全在本技能 `references/` 者事实层:席内达档复核过落地前检三条即转正式入队。
2828
- 其余为规则层:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。
2929
- ⛔ 两层不由席位批准;清标即落地同受此闸,漏判会被队列守卫在 merge group 里拒收。

0 commit comments

Comments
 (0)