Skip to content

Commit 6212cc6

Browse files
chore(governance): the human-merge line threshold is 3,000 changed lines (HUMAN_MERGE_LINE_THRESHOLD), per the maintainer's 2026-10-09 ruling (#22490)
Fixes #22451 Clause-②: no ## Summary The maintainer, 2026-10-09, verbatim and untranslated: 「这种大额改动就应该人工审核,我还是建议5000行的人工审核门槛降到 3000,立卡。」 `HUMAN_MERGE_LINE_THRESHOLD` moves from 5000 to 3000 changed lines (`additions + deletions`, generated files included). It stays declared ONCE, in `scripts/pm/check-governed-merges.mjs`; the predicate stays STRICTLY greater (exactly 3,000 is under, 3,001 is over). Nothing else about the rule changes: the four-piece terminal set, no Tier S exception, generated files included, the per-repo coverage of `GOVERNED_REPOS`. The earlier maintainer quotes (2026-09-18 and 2026-09-27) stay verbatim everywhere they were; the 2026-10-09 words are added beside them, verbatim. No ADR records the number (`git grep` over `docs/adr/**` and `docs/NORTH-STAR.md`: zero hits on the threshold), so no ADR amendment is owed. Lineage recorded on the card: 2026-09-18 「修改代码量超过某个行数(比如5000)就应该人工审核」; 2026-09-27 「所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。」; 2026-10-09 the words above. PR #22215 (the occasion) is unaffected: no retroactive review of anything already approved or merged. Notation in this body: the greater-than sign is spelled `[over]` wherever a source line carries it, because the platform's body sanitizer eats angle-bracket-shaped fragments. ## Every edit: site → old → new (file:line as on this head, `cfff9b1a`) | site | old | new | |---|---|---| | `scripts/pm/check-governed-merges.mjs:183` — header section title | `(maintainer ruling, 2026-09-18)` | `(maintainer ruling, 2026-09-18; the line, 2026-10-09)` | | `…check-governed-merges.mjs:189-191` — header | (the 2026-09-18 quote alone) | the 2026-10-09 words added beside it, verbatim | | `…check-governed-merges.mjs:193` — header prose | `exceeds 5,000` | `exceeds 3,000` | | `…check-governed-merges.mjs:198-201` — header prose | `「比如」 makes 5,000 the ruled DEFAULT, declared once as …` | `「比如」 made the 2026-09-18 figure the ruled DEFAULT; the 2026-10-09 word lowered it to 3,000. It is declared once as …` | | `…check-governed-merges.mjs:1102-1114` — docblock on the declaration | the 2026-09-18 ruling as the ruled default; `5,000 changed lines is under the threshold, 5,001 is over` | adds the 2026-10-09 words verbatim; `3,000 changed lines is under the threshold, 3,001 is over` | | `…check-governed-merges.mjs:1115` — **the ONE declaration** | `export const HUMAN_MERGE_LINE_THRESHOLD = 5000;` | `export const HUMAN_MERGE_LINE_THRESHOLD = 3000;` | | `…check-governed-merges.mjs:5989` — self-test pin, the ruled default | `the-threshold-is-the-ruled-default-5000-declared-once`, `=== 5000` | `the-threshold-is-the-ruled-3000-of-2026-10-09-declared-once`, `=== 3000` | | `…check-governed-merges.mjs:5992-5993` — pin, exactly at the line | `exactly-5000-changed-lines-is-UNDER-the-threshold`, `changedLines === 5000` | `exactly-3000…`, `changedLines === 3000` | | `…check-governed-merges.mjs:5994-5995` — pin, one over | `5001-changed-lines-is-OVER-it…`, `changedLines === 5001` | `3001-changed-lines-is-OVER-it…`, `changedLines === 3001` | | `…check-governed-merges.mjs:6062-6063` — pin, threshold unchanged by the lift | `threshold === 5000`, `additions: 5000` (twice) | `threshold === 3000`, `additions: 3000` (twice) | | `…check-governed-merges.mjs:6176` — pin name, `--test` over the line | `--test-with-5001-changed-lines-on-an-ordinary-path-exits-3-and-says-HUMAN-MERGE` | `--test-with-3001-changed-lines-…` (the run itself hands in `String(HUMAN_MERGE_LINE_THRESHOLD)` + 1 deletion) | | `…check-governed-merges.mjs:6182` — pin name, `--test` exactly at | `and-exactly-5000-exits-0-the-threshold-is-strictly-greater` | `and-exactly-3000-exits-0-…` | | `…check-governed-merges.mjs:6184`, `:6189` — pin, `--json` over the line | `'--additions', '5001'`, `changedLines === 5001` | `'--additions', '3001'`, `changedLines === 3001` | | `…check-governed-merges.mjs:6340`, `:6344`, `:6368-6369`, `:6381-6382`, `:6390` — fixture commit subjects and every reader of them | `(#5000)` / `(#5001)`; `PR #5001` / `PR #5000`; `pr === 5001` | `(#3000)` / `(#3001)`; `PR #3001` / `PR #3000`; `pr === 3001` — the fixture's PR numbers are mnemonics for the line the commit stands at (`lines(LINE)` and `lines(LINE + 1)`, `LINE = HUMAN_MERGE_LINE_THRESHOLD`) | | `…check-governed-merges.mjs:6717` — self-test summary line | `the ruled 5,000 declared once` | `the ruled line, 3,000 since the maintainer's 2026-10-09 word, declared once` | | `scripts/pm/check-governed-queue-guard.mjs:335-338` — header | (the 2026-09-18 quote alone) | the 2026-10-09 words added beside it, verbatim | | `…check-governed-queue-guard.mjs:340` — header prose | `HUMAN_MERGE_LINE_THRESHOLD (5,000) is declared there ONCE` | `HUMAN_MERGE_LINE_THRESHOLD (3,000) is declared there ONCE` | | `…check-governed-queue-guard.mjs:1913` — printed REFUSED guidance | the 2026-09-18 and 2026-09-27 rulings listed | the 2026-10-09 words listed beside them, verbatim | | `…check-governed-queue-guard.mjs:3463-3466` — pin on that guidance | `…quoting-both-rulings-untranslated…`, two `includes` | `…quoting-all-three-rulings-untranslated…`, a third `includes` on the 2026-10-09 words | | `scripts/pm/check-skill-line-ratchet.mjs:325-328` — ceiling-ledger comment describing the rule line | `a PR over 5,000 changed lines (additions + deletions, generated files included)` | `a PR over the human-merge line (3,000 changed lines since the maintainer's 2026-10-09 word; additions + deletions, generated files included)` | | `scripts/pm/dispatch-gates.mjs:26400` — the ONE pin (FROZEN file; the constant move breaks this pin, the freeze's own exception) | `…and it is the ruled 5000`, `HUMAN_MERGE_LINE_THRESHOLD === 5000`, `additions: 5001` | `…and it is the ruled 3000`, `=== 3000`, `additions: 3001` — nothing else in the file: no rewording, no other pin, no `--tier` note | | `.claude/skills/pm-dispatch/SKILL.md:186` — the rule line | `改动 [over]5000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR additions+deletions。` | `改动 [over]3000 行…` — the digit in place: 319/319 lines, widest row 342 bytes, 0 net lines, no re-wrap | | `.claude/skills/pm-dispatch/references/landing-operations.md:58` | `[over]5000 行(含生成物)照 Tier H。` | `[over]3000 行(含生成物)照 Tier H。` — the digit in place: 101/101 lines, 0 net lines | | `AGENTS.md:510` — Multi-agent discipline §7 (c) | `changed lines exceed 5,000` | `changed lines exceed 3,000` — in place, 1108 lines before and after | ### Readers that follow the constant unchanged (PM hypothesis 1, measured on this head) `git grep -n HUMAN_MERGE_LINE_THRESHOLD` — every code reader reads the one declaration: `check-governed-merges.mjs` `sizeVerdict` `:1582` / `:1585` / `:1591`, the sweep's printed words `:3377` / `:3383`, the self-test `:5990-5991`, `:6012`, `:6175`, `:6181`, `:6251`; `check-governed-queue-guard.mjs` import `:499`, printed CLEAR line `:1880`, `:3269`, and its own pin `:3659` (`this-file-declares-NO-threshold-of-its-own`); `dispatch-gates.mjs` import `:467`, `:13145`, `:26392-26396`, `:26594`, `:28568`. objectui and cloud carry no copy (the card's reading; `GOVERNED_REPOS` covers them from this script). ### The literal pin over the seven touched files `git grep -n -E '\b500[01]\b|5,000'` on `cfff9b1a` over the seven files: 23 hits, every one a quoted maintainer ruling, a parse-only probe, or one regex artifact — - quoted rulings, verbatim and untouched: `check-governed-merges.mjs:187` (2026-09-18), `:191` and `:1106` (2026-10-09, the words added), `:260` (2026-09-27), `:1103` (the 「比如5000」 fragment the docblock always quoted); `check-governed-queue-guard.mjs:333`, `:337`, `:392`, `:1867`, `:1911`, `:1912`, `:1913`, `:3464`, `:3465`, `:3466`, `:3553`; `check-skill-line-ratchet.mjs:324` (the 2026-09-18 quote); - parse-only probes, unchanged on purpose: `check-governed-merges.mjs:6003` (a string `'5001'` is NOT MEASURED), `:6107` / `:6109` (`--additions 5000 --deletions 1` parse as the pair with the CALLER as source), `:6196` (half a pair, `--additions 5001` alone, is refused); - one regex artifact: `check-skill-line-ratchet.mjs:779` — `15,000` (the authenticated REST quota) contains `5,000` as a substring; not the threshold, untouched. No prose restatement outside a quote carries the old figure any more. ## The measurement the card asks for: merged pull requests of the last 30 days between 3,001 and 5,000 changed lines **Window and filter, stated exactly.** Repository `objectstack-ai/objectstack` only. Window: `merged_at` in [2026-09-09T12:16:51Z, 2026-10-09T12:16:51Z] (30 days back from the reading, taken at 2026-10-09T12:16:51Z). Source: `GET /repos/objectstack-ai/objectstack/pulls?state=closed&sort=updated&direction=desc&per_page=100`, paged until a page's oldest `updated_at` fell before the window start (27 pages, 2,700 closed pull requests read; a merge inside the window implies `updated_at` at or after it, so no later page can hold one); then one `GET /repos/objectstack-ai/objectstack/pulls/{n}` per candidate for `additions` / `deletions` (the list object omits them). Changed lines = `additions + deletions` as GitHub reports them, generated files included — the same number the predicate reads. ⛔ This is a reading of one window in one repository, not a claim about every pull request ever merged; objectui and cloud are not in it. - candidates (merged inside the window): **2606**; sized: **2606**; per-PR reads that failed: **0** - at or under 3,000 changed lines (unaffected by the move): **2579** - **3,001–5,000 changed lines — the population the new line adds: 16** (≈ 0.5 per day; 0.6% of the window's merges) - over 5,000 changed lines (already on the human-merge line before this change): **11** — 19024, 18971, 17076, 20639, 21352, 20125, 19803, 18638, 17372, 22002, 21084 | PR | changed lines | +additions / −deletions | files | merged (UTC) | title | |---|---|---|---|---|---| | 19728 | **4882** | +4813 / −69 | 22 | 2026-09-24 | feat(formula,objectql): read one hop through a lookup in a validation predicate | | 21222 | **4307** | +1066 / −3241 | 46 | 2026-10-01 | feat(spec,rest,lint)!: retire the form field's publicPicker and the anonymous lo… | | 19921 | **4220** | +730 / −3490 | 40 | 2026-09-24 | fix(sdui-manifest): one producer from objectui's built tree; retire the declarat… | | 19738 | **3761** | +1962 / −1799 | 25 | 2026-09-23 | docs(skills): split the four over-500-line SKILL.md into routing + references, T… | | 21377 | **3646** | +3557 / −89 | 36 | 2026-10-02 | feat(automation): a flow's credentials live in a write-only channel on the secre… | | 20236 | **3581** | +2427 / −1154 | 14 | 2026-09-27 | fix(runtime,rest): the dispatcher /meta item reads ask the per-caller read gate … | | 21946 | **3567** | +3560 / −7 | 6 | 2026-10-06 | ci(merge-queue): name a no-runner red as infra:no-runner, with a once-per-head r… | | 19737 | **3460** | +405 / −3055 | 22 | 2026-09-23 | pm: retire the needs:contract-review label from the tree (ruling B step ③) | | 17454 | **3380** | +2171 / −1209 | 45 | 2026-09-12 | feat(auth)!: adopt better-auth's account-issuer rollback — drop sys_account.issu… | | 17334 | **3368** | +3315 / −53 | 44 | 2026-09-10 | fix(triggers,spec,service-automation)!: a time-triggered flow declares its actin… | | 22193 | **3320** | +1750 / −1570 | 56 | 2026-10-08 | feat(spec)!: a page gains an optional print declaration and a linted printable b… | | 19725 | **3227** | +3156 / −71 | 12 | 2026-09-22 | feat(pm): fleet-write relay — closed op table, validator, executor, seat-side di… | | 20532 | **3122** | +2419 / −703 | 76 | 2026-09-29 | fix(spec): shard the generated liveness and strictness counts so PRs moving diff… | | 20903 | **3090** | +1960 / −1130 | 51 | 2026-09-30 | feat(spec)!: connector-attached sync leaves the connector — syncConfig / fieldMa… | | 18198 | **3071** | +2632 / −439 | 48 | 2026-09-16 | feat(types,triggers,service-automation,runtime,cli,spec,lint)!: package-authored… | | 20194 | **3044** | +970 / −2074 | 46 | 2026-09-27 | feat(spec)!: retire the export-job API family, IExportService and ScheduleState … | (PR numbers are written as plain digits on purpose: titles in this repository begin with `fix`/`chore`/…, and a closing keyword beside a `#`-reference would make the platform close that card on merge.) ## Gates: the derived union, every exit captured before any pipe, reconciled with `--ran` on head `cfff9b1a` Derivation: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on `cfff9b1a` — its first stderr line names the tree ("gate list derived from the tree of 'objectstack-ai/objectstack' at commit cfff9b1"), the change set is 7 path(s) three-dot off merge base `f66c440de` of `origin/main` (committed 7, working tree 0, untracked 0). 45 commands derived. Each ran as printed, `eval "$cmd" ... ; status=$?` before any pipe, recorded as `cmd :: exit N`; the battery ran to completion on the final head with its exit captured to a file (`pnpm check:pm-dispatch-gates`: `✓ dispatch-gates self-test: 2011 cases pass.` — 1080.2s on this box; an earlier bare `nohup` run on the same head also printed `2011 cases pass` but its exit was never captured, so it is not the record). Reconciliation (`node scripts/pm/dispatch-gates.mjs --ran ran.list`, exit 0): `Run reconciliation — 45 derived, 45 run, 0 NOT-MEASURED, 0 UNRUN.` · `✓ dispatch-gates --ran: 45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED (a DERIVED zero — all 45 recorded an exit code and none of them is 3).` | # | command | exit | |---|---|---| | 1 | `node scripts/check-ci-filter-parity.mjs` | 0 | | 2 | `node scripts/check-closing-keyword-parity.mjs` | 0 | | 3 | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | | 4 | `node scripts/check-comment-mask-corpus.mjs` | 0 | | 5 | `node scripts/check-declaration-mirrors.mjs` | 0 | | 6 | `node scripts/check-declaration-mirrors.mjs --self-test` | 0 | | 7 | `node scripts/check-scripts-symbol-anchors.mjs` | 0 | | 8 | `node scripts/check-scripts-symbol-anchors.mjs --self-test` | 0 | | 9 | `node scripts/check-self-test-wired.mjs` | 0 | | 10 | `node scripts/check-self-test-wired.mjs --self-test` | 0 | | 11 | `node scripts/check-self-test-workflow-commands.mjs` | 0 | | 12 | `node scripts/check-self-test-workflow-commands.mjs --self-test` | 0 | | 13 | `node scripts/check-skills-token-ratchet.mjs` | 0 | | 14 | `node scripts/check-skills-token-ratchet.mjs --self-test` | 0 | | 15 | `node scripts/check-whole-set-label-write.mjs` | 0 | | 16 | `node scripts/check-whole-set-label-write.mjs --self-test` | 0 | | 17 | `node scripts/pm/bare-root-worklist.mjs --self-test` | 0 | | 18 | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | | 19 | `node scripts/pm/check-harness-current.mjs --self-test` | 0 | | 20 | `pnpm check:agent-test-spelling` | 0 | | 21 | `pnpm check:bash32-floor` | 0 | | 22 | `pnpm check:cli-command-ids` | 0 | | 23 | `pnpm check:closing-target-claim` | 0 | | 24 | `pnpm check:cross-package-test-inputs` | 0 | | 25 | `pnpm check:declared-population-live` | 0 | | 26 | `pnpm check:doc-authoring` | 0 | | 27 | `pnpm check:docs-audit-scope` | 0 | | 28 | `pnpm check:driver-memory-census` | 0 | | 29 | `pnpm check:entry-guard` | 0 | | 30 | `pnpm check:gitlink-declared` | 0 | | 31 | `pnpm check:nul-bytes` | 0 | | 32 | `pnpm check:parse-guard` | 0 | | 33 | `pnpm check:pm-expected-skips` | 0 | | 34 | `pnpm check:pm-governed-merges` | 0 | | 35 | `pnpm check:pm-governed-prose` | 0 | | 36 | `pnpm check:pm-skill-id-lint` | 0 | | 37 | `pnpm check:pm-skill-ratchet` | 0 | | 38 | `pnpm check:pnpm-filter-targets` | 0 | | 39 | `pnpm check:ratchet-remedy-authority` | 0 | | 40 | `pnpm check:refd-timer-probe` | 0 | | 41 | `pnpm check:required-contexts` | 0 | | 42 | `pnpm check:skill-frame-sync` | 0 | | 43 | `pnpm check:watch-hint-literal` | 0 | | 44 | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 | | 45 | `pnpm check:pm-dispatch-gates` | 0 | Notes on the run: - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first answered **exit 3 — PREREQUISITE NOT MET** (`@objectstack/formula` and `@objectstack/lint` not built; "Nothing was measured"). That is not a measurement, so it is not the record: the prescribed `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` ran under the shared verify lock (`os-verify-lock: VERDICT command-exit 0 · held the lock 186s · waited 0s`), and the gate re-ran: exit 0. The row above is that re-run. - The diff touches no workspace package (every path is root-owned), so no package `build`/`test`/`typecheck` is owed locally and none was run beyond that prerequisite build; `pnpm lint` (the repo-wide eslint sweep) is CI's run, not derived here, and was not run. - The four skill/prose gates named by the card (`check:pm-skill-ratchet`, `check:pm-skill-id-lint`, `check:pm-governed-prose`, `check:nul-bytes`) are in the union and green; `check:pm-skill-ratchet` reads `SKILL.md` 319/319 and `landing-operations.md` 101/101 with headroom 0, unchanged. - CI convergence is the PM's to read; this is the local half. ## Reverse verification: the constant alone back to 5000, every pin left at 3000 PM hypothesis 2 asked for the pins to be found by moving the constant first. Measured as a mutation leg instead, in a throwaway detached worktree at `cfff9b1a` (installed; `node_modules` removed and the worktree removed afterwards), through `scripts/ablation-replace.mjs` so the on-disk evidence is the tool's own: anchor `export const HUMAN_MERGE_LINE_THRESHOLD = 3000;` x1 → x0, replacement `… = 5000;` x0 → x1, blob `6d8be7178d5f` → `717e33fc3d6d`, "ok mutation landed"; after every leg, "ok restored: blob == HEAD (6d8be71) and `git diff HEAD` is empty", and a separate `git status --porcelain` read of 0 lines. Expected direction: RED on the pins that bind the number, GREEN where the number is only imported. | leg | command under the mutation | result | |---|---|---| | 1 | `node scripts/pm/check-governed-merges.mjs --self-test` | **exit 1 — 5 failure(s)**: `the-threshold-is-the-ruled-3000-of-2026-10-09-declared-once: 5000`; `⭐ exactly-3000-changed-lines-is-UNDER-the-threshold` (read `changedLines: 5000, threshold: 5000`); `⭐ 3001-changed-lines-is-OVER-it-the-count-is-additions-PLUS-deletions` (read `changedLines: 5001`); `and-the-threshold-the-strict-comparison-and-the-generated-files-inclusion-are-UNCHANGED-by-the-lift`; `--json-carries-governed-false-humanMerge-true-and-the-size-block` (3,001 handed in read `threshold: 5000, exceeds: false`) | | 2 | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | **exit 0 — 296 cases pass**: this file declares no threshold of its own (its own pin `this-file-declares-NO-threshold-of-its-own` forbids one) and reads `LINE = HUMAN_MERGE_LINE_THRESHOLD` through the import, so it follows the constant wherever it is — green by design, not a vacuous pin | | 3 | `node scripts/pm/dispatch-gates.mjs --self-test` | **exit 1 — 1 of 2011 case(s) failed**: `✗ the threshold is read from the gate — no second copy here — and it is the ruled 3000` — the one pin at `:26400`, and nothing else in the frozen file | So the number binds in exactly six places outside its declaration — five merges pins and the one dispatch-gates pin — which are exactly the rows this PR moved; on the unmutated head all three self-tests are green (below). ## Acceptance notes - PM hypothesis 2 (move the constant first, repair what reds): executed as one coherent edit set, and then measured the other way round by the mutation leg above — the constant alone back to 5000 reds exactly five merges pins and the one dispatch-gates pin; the queue guard pins no number of its own (its `:3659` pin forbids one), so it stays green by design. Same information, reported as a measurement. - `check-skill-line-ratchet.mjs:325-328`: a code comment inside a ceiling-ledger entry; re-wrapped 3 → 4 lines (a `.mjs` file, no line ceiling). It names the lowered line without restating the old figure, so the literal pin stays clean. - `check-governed-queue-guard.mjs:3463`: the pin's name `quoting-both-rulings` became `quoting-all-three-rulings` and gained a third `includes`, so the new printed words are pinned exactly like the earlier two. Battery floors are by battery name and count; the case count is unchanged (296). - The fixture PR numbers in `check-governed-merges.mjs` (`#5000` / `#5001`) were mnemonics for the line a synthetic commit stands at; moved to `#3000` / `#3001` so the pin reads true against the live `LINE` and the literal grep stays clean. Synthetic repo, no real PR named. - `dispatch-gates.mjs` (FROZEN, ruling 208 R6): the one pin at `:26400` only, digits only. The `--tier` note from the earlier card is not added; it waits on the letter of its own card (#22453). - `15,000` at `check-skill-line-ratchet.mjs:779` matches the `5,000` alternation as a substring; a quota figure, untouched. - Changeset: none, `skip-changeset` applies — root `package.json` is `private: true`; none of the seven touched paths is owned by any of the 132 workspace packages; positive control: `packages/spec/src/index.ts` is owned by `@objectstack/spec` (`private: false`, `files[]` present). `scripts/pm/**`, `.claude/**` and `AGENTS.md` publish nothing. - Shared checkout `/home/user/objectstack` untouched at `f66c440d`; worktree only. - Nothing found out of scope; nothing filed. ## 维护者速读(草稿) ### 改了什么 把「超过多少行就必须人工审核」这条线从 5000 行降到 3000 行(additions + deletions,含生成物)。数字只在 `scripts/pm/check-governed-merges.mjs` 里声明一次,队列守卫、派发脚本、自检、技能文件与 `AGENTS.md` 都跟着这一处读;自检的钉子同步挪到 3000/3001;三条早前的原话原样保留,10 月 9 日的原话加在旁边。 ### 为什么改 按维护者 2026-10-09 的裁决原话执行:「这种大额改动就应该人工审核,我还是建议5000行的人工审核门槛降到 3000,立卡。」 字面已给定,不是决策卡。 ### 风险与代价(含回滚) - 代价:落地后,3001–5000 行之间的 PR 从「绿了就能排队合并」变成「要维护者 APPROVED(或人工直合)才能落地」。本 PR 正文的 30 天实测给出了这个区间的 PR 数量,即新增的审核负担。 - 风险:低。规则其余部分一字未动(四件套、无 Tier S 例外、含生成物、各仓覆盖)。`dispatch-gates.mjs` 冻结文件只动了那一枚因常量搬家而必红的钉子。 - 回滚:把那一处常量改回 5000 并把自检钉子一并改回即可(本 PR 的反向镜像);不涉及任何发布面,无 changeset。 ### 席位意见 (留空) ### 你要做的 本 PR 触及 Tier H(`AGENTS.md`)与 Tier S(`.claude/**`),按治理规则留在 draft:维护者给出 APPROVED 后由认领席落地,或由维护者人工直合。不需要你做别的决定。 --- _Generated by [Claude Code](https://claude.ai/code/session_01JmWtcHfGbC4ncw4GFKWuRA)_ Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent d2e7d6c commit 6212cc6

7 files changed

Lines changed: 57 additions & 40 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
183183
- governed 面统一定义:`docs/adr/**` + `.claude/**`(全量)+ `skills/**` + `docs/NORTH-STAR.md`。
184184
- governed 面同含 `AGENTS.md` + `CLAUDE.md`;`GOVERNED_REPOS` 各仓同治理待遇,执行席恒随落地仓车道。
185185
- 路径面命中 Tier H ⇒ ACCEPT 换终局四件套,混合 diff ⛔ 不按比例判;要拆让 dev 单独开 PR。
186-
- 改动 >5000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR additions+deletions。
186+
- 改动 >3000 行(含生成物)同换终局四件套,⛔ 无 Tier S 例外;读数 = PR additions+deletions。
187187
- ① 复核结论照常写在 issue 上;技能面 hunk 须由契约复审档的席复核,档外席先交 skills 席。
188188
- ② PR 留给维护者看得见地悬着;终局两条:人工直合即审核记录;授权批准 ⇒ 席位落地。
189189
- 看得见 = 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
@@ -55,7 +55,7 @@
5555
- 确认 MERGED 要两个读数:每轮同时读队列分支与 `origin/main`。
5656
- 契约复核 PASS 落地的 PR 到窗口时已 ready 且 auto-merge 在挂,见 `contract-review.md`。
5757
- 窗口自身权责不变:跟到 MERGED、踢出处置、落地后对账。
58-
- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>5000 行(含生成物)照 Tier H。
58+
- 转 ready/入队前跑 `check-governed-merges.mjs --pr N`:受管面照两层;>3000 行(含生成物)照 Tier H。
5959
- 受管路径全在 `.claude/**` 者 Tier S:席内达档复核过落地前检三条即转正式入队。
6060
- Tier H(其余受管面)者:四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。
6161
- ⛔ 两层不由席位批准;清标即落地同受此闸,漏判会被队列守卫在 merge group 里拒收。

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ Even inside your own worktree, operate defensively:
507507
⛔ **Three classes of PR never enter this path on green alone:** (a) a diff touching any **governed surface**
508508
(**Prime Directive #14**, which names them and holds the current list — **this file and `CLAUDE.md` are on it**,
509509
so re-read it rather than recalling it); (b) the **Version Packages** PR, or any PR whose merge performs a
510-
release (**Prime Directive #15**); (c) a PR whose **changed lines exceed 5,000** (`additions + deletions`,
510+
release (**Prime Directive #15**); (c) a PR whose **changed lines exceed 3,000** (`additions + deletions`,
511511
generated files included) — it lands the way a Tier H surface does: an authorized APPROVED review and then
512512
the owning seat, or a human merge. Read the PR's file list (`get_files`), **its author and its size** before
513513
you arm anything.

‎scripts/pm/check-governed-merges.mjs‎

Lines changed: 40 additions & 30 deletions
Large diffs are not rendered by default.

‎scripts/pm/check-governed-queue-guard.mjs‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,12 @@
332332
*
333333
* > 修改代码量超过某个行数(比如5000)就应该人工审核
334334
*
335+
* and on 2026-10-09 lowered the line, verbatim and untranslated:
336+
*
337+
* > 这种大额改动就应该人工审核,我还是建议5000行的人工审核门槛降到 3000,立卡。
338+
*
335339
* PR #19033 landed it seat-side: `testVerdict(paths, { size })` in the sibling
336-
* carries a `size` limb, `HUMAN_MERGE_LINE_THRESHOLD` (5,000) is declared
340+
* carries a `size` limb, `HUMAN_MERGE_LINE_THRESHOLD` (3,000) is declared
337341
* there ONCE, the comparison is `additions + deletions` STRICTLY greater, and
338342
* generated files are INCLUDED — the case that prompted the ruling, PR #18971
339343
* (+238,310 / −119, 237,706 of them regenerated), touched no governed path and
@@ -1906,6 +1910,7 @@ export function renderSizeVerdict(verdict) {
19061910
'',
19071911
' 2026-09-18 「修改代码量超过某个行数(比如5000)就应该人工审核」',
19081912
' 2026-09-27 「所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。」 (objectstack#20153)',
1913+
' 2026-10-09 「这种大额改动就应该人工审核,我还是建议5000行的人工审核门槛降到 3000,立卡。」',
19091914
'',
19101915
' landed as `HUMAN_MERGE_LINE_THRESHOLD` and the lift in scripts/pm/check-governed-merges.mjs, which',
19111916
' this leg IMPORTS — the number, the comparison, what counts and what lifts are that file\'s, never',
@@ -3455,9 +3460,10 @@ export async function selfTest() {
34553460
text18971,
34563461
);
34573462
assert(
3458-
'and-names-the-TWO-landings-an-authorized-APPROVAL-or-a-HUMAN-MERGE-quoting-both-rulings-untranslated-and-never-advises-shrinking-the-diff',
3463+
'and-names-the-TWO-landings-an-authorized-APPROVAL-or-a-HUMAN-MERGE-quoting-all-three-rulings-untranslated-and-never-advises-shrinking-the-diff',
34593464
text18971.includes('HUMAN MERGE') && text18971.includes('修改代码量超过某个行数(比如5000)就应该人工审核') &&
3460-
text18971.includes('所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。') && text18971.includes('DRAFT') && !/shrink|split the|smaller/i.test(text18971),
3465+
text18971.includes('所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。') &&
3466+
text18971.includes('这种大额改动就应该人工审核,我还是建议5000行的人工审核门槛降到 3000,立卡。') && text18971.includes('DRAFT') && !/shrink|split the|smaller/i.test(text18971),
34613467
text18971,
34623468
);
34633469
assert(

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,10 @@ export const CEILINGS = new Map([
322322
// 2026-09-18, live chat beside ruling C on the regenerated-artefact landing,
323323
// verbatim and untranslated: 「还有应该完善skills,修改代码量超过某个行数
324324
// (比如5000)就应该人工审核。」 The content is ONE rule line beside the
325-
// four-piece-terminal trigger: a PR over 5,000 changed lines (additions +
326-
// deletions, generated files included) takes the same terminal, with no
327-
// fact-layer exception; the mechanical face is `check-governed-merges.mjs`.
325+
// four-piece-terminal trigger: a PR over the human-merge line (3,000 changed
326+
// lines since the maintainer's 2026-10-09 word; additions + deletions,
327+
// generated files included) takes the same terminal, with no fact-layer
328+
// exception; the mechanical face is `check-governed-merges.mjs`.
328329
// It could not be paid in place — measured, not assumed: the trigger line it
329330
// belongs beside stands at 118 B against the 120-byte cap and the shortest
330331
// self-contained form of the rule is 111 B; of the file's 598 adjacent bullet

‎scripts/pm/dispatch-gates.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26397,7 +26397,7 @@ function selfTest() {
2639726397
t('exactly at the threshold is under it — strictly greater, as the gate reads it', atLine.includes('under.') && !atLine.includes('HUMAN MERGE'), atLine);
2639826398
const noneLine = changedLineLines(null).join('\n');
2639926399
t('an explicit path list is NOT MEASURED, said out loud, never a silent under', noneLine.includes('NOT MEASURED') && !noneLine.includes('under.') && noneLine.includes('--pr'), noneLine);
26400-
t('the threshold is read from the gate — no second copy here — and it is the ruled 5000', HUMAN_MERGE_LINE_THRESHOLD === 5000 && changedLineLines({ additions: 5001, deletions: 0 }).join('\n').includes('OVER'));
26400+
t('the threshold is read from the gate — no second copy here — and it is the ruled 3000', HUMAN_MERGE_LINE_THRESHOLD === 3000 && changedLineLines({ additions: 3001, deletions: 0 }).join('\n').includes('OVER'));
2640126401
t('a text buffer counts its lines, an unterminated last line included', lineCountOf(Buffer.from('a\nb\n')) === 2 && lineCountOf(Buffer.from('a\nb')) === 2 && lineCountOf(Buffer.alloc(0)) === 0);
2640226402
t('a buffer with a NUL in its first 8000 bytes is binary: null, which the caller counts as zero lines', lineCountOf(Buffer.from([0x61, 0, 0x62])) === null);
2640326403
// Same liveness guards as the mandatory table: dead data reading as

0 commit comments

Comments
 (0)