Repository navigation
Commit 1f33392
feat(governed): an authorized APPROVED review lifts the over-5000-line SIZE limb, as it lifts a Tier H path (#20159)
Fixes #20153
Clause-②: no
## What this lands
The maintainer's ruling of 2026-09-27 (card #20153, verbatim,
untranslated): 「所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。」
**An authorized APPROVED review now lifts the SIZE limb exactly as it
lifts a Tier H path.** "Authorized" is an account in
`GOVERNED_APPROVERS`; the predicate is the governed leg's own —
latest-decisive review per account, on ANY commit (the 2026-09-04
unpinning), dismissed / superseded / unauthorized approvals never count,
an unreadable review list fails closed. After the approval the owning
seat lands the PR through the queue; the maintainer's direct merge stays
the other landing.
Unchanged, as the card requires: the threshold (5,000), the strict
comparison, "generated files included", the FORK limb (no approval lifts
a fork), the post-merge sweep (it still lists every oversized landing —
it hands the predicate no approval), `GOVERNED_APPROVERS`, the Tier S
rule, and the exit-code register (no new code).
### Mechanism — one derivation, one predicate, imported
- `scripts/pm/check-governed-merges.mjs` (the predicate's home):
`testVerdict(paths, { size, approval })` takes the approval verdict as
DATA; `sizeVerdict(size, approval)` adds `lifted` (approvers, the commit
each approval was given on, the ruling's citation) and `approvalState`;
`sizeLiftFrom` reads only the verdict's `state` and its `approvals`
field; `sizeLimbFires(size)` = over the line AND not lifted;
`landsByHumanMerge` reads it. `exceeds` still reads true over the line —
the lift is a second fact beside the number, never a change to the
number.
- `scripts/pm/check-governed-queue-guard.mjs` (the queue leg): the SIZE
leg REUSES the governed leg's `authorizedApprovalVerdict` object for a
pull request that leg already read, else runs the same derivation on one
review read of its own — only for a pull request that is OVER the line
(a within PR and an unreadable size make no review read; both pinned
with throwing spies). `authorizedApprovalVerdict` now also returns
`approvals: [{ login, commitId }]` — the field only the authorized
reduction carries, so the generic `approvalVerdict` (which never asked
WHO) cannot lift. The guard spells no lift condition of its own; a
self-test pin reads its source to prove it.
- Exit codes: lifted → 0 (the SIZE block prints approver, commit, and
`objectstack#20153` with the ruling verbatim); no / unauthorized /
dismissed / superseded approval → 8, as today; unreadable size → 9, as
today; **an unreadable review list on an oversized PR → 8** (over the
line, no lift proven; the words say the list could not be read). Code 9
stays "the SIZE could not be read". Precedence governed-then-size is
unchanged.
- The seat-side `check-governed-merges.mjs --pr N` reads no reviews (it
never did, for the PATH limb either, and it cannot import the guard's
derivation — the module cycle is measured in the file). It answers the
SIZE question the way it answers the PATH question: over the line is
exit 3 and the words name the two landings the queue leg then holds the
PR to. On the same PR the two tools read one predicate; the queue
additionally holds the approval record — the same relationship they have
on a Tier H path today.
### PM mechanism assumptions — what the tree said
- Assumption 3 (the authorized-approval derivation is exported by
`check-governed-merges.mjs` and imported by the guard): **falsified**.
`authorizedApprovalVerdict` and `GOVERNED_APPROVERS` live in the guard;
the guard imports the sibling at module scope, and the reverse edge
(static or lazy) deadlocks (`Detected unsettled top-level await`,
measured in both files' headers). There is still exactly ONE derivation
— it stays in the guard; the sibling receives its result as data and
re-derives nothing. No second copy was added.
- Assumption 4 (protocol text): `AGENTS.md` class (c) stated the old
rule and is rewritten (same three-line block, +1 line; ratchet ceiling
1116, now 1106). `.claude/skills/pm-dispatch/SKILL.md:187` (「改动超 5000
行(含生成物)同换终局四件套」) and `references/landing-operations.md:58` (「超 5000
行(含生成物)照 Tier H」) already route an oversized PR to the Tier H terminal,
whose two landings line 189 / line 60 already spell (「授权批准 ⇒ 席位落地」,
「获授权批准后认领席落地」) — consistent, untouched, so no `.claude/**` file changes
in this PR. `scripts/pm/dispatch-gates.mjs`'s dispatch-time line said
"lands only by a HUMAN MERGE" and is rewritten (its self-test pin
updated).
- Assumption 5 (`--pr 20125` as a reading): NOT MEASURED from this
container — the changed-files walk answered HTTP 403 on page 2 and the
tool refused rather than answering on a subset (its documented
behaviour). By construction it would answer exit 3 with the words naming
both landings, and the queue leg would answer 0 on `os-zhuang`'s
approval on `e4ead748` (the self-test replays exactly that shape).
- Assumption 8 (unreadable review list): landed as exit 8, pinned in
both scripts.
### Grep table — `5,000` / `5000` / `HUMAN_MERGE_LINE_THRESHOLD` / `size
limb` over `scripts/pm/**`, `AGENTS.md`, `.claude/**` at `8d1f7ab7`
| file | verdict | note |
|---|---|---|
| `scripts/pm/check-governed-merges.mjs` | changed | predicate, header,
words, 13 new pins (battery floor 30 → 44) |
| `scripts/pm/check-governed-queue-guard.mjs` | changed | SIZE leg,
header, words, 13 new pins + 3 rewritten (battery floor 30 → 54) |
| `scripts/pm/dispatch-gates.mjs` | changed | `changedLineLines` OVER
text stated "lands only by a HUMAN MERGE"; its pin updated |
| `AGENTS.md` | changed | Multi-agent §7 class (c) stated "lands only by
a human merge" |
| `.claude/skills/pm-dispatch/SKILL.md` | consistent | line 187 routes
an oversized PR to the 四件套 terminal; line 189 names its two landings |
| `.claude/skills/pm-dispatch/references/landing-operations.md` |
consistent | line 58 「照 Tier H」; line 60 names Tier H's two landings |
| `scripts/pm/check-half-states.mjs` | consistent | "a human merge or an
authorized approval is the review record"; other hits are rate-limit
numbers |
| `scripts/pm/check-skill-line-ratchet.mjs` | unrelated | a
ceiling-ledger comment narrating the 2026-09-18 raise (history, not a
rule statement) |
| `scripts/pm/check-prior-rulings.mjs` | unrelated | "5,000 comments"
page cap |
| `.claude/skills/pm-dispatch/references/platform-readings.md`,
`references/rest-channel.md` | unrelated | rate-limit quotas (5000/h) |
| `board-snapshot.mjs`, `ci-failure.mjs`, `close-cards.mjs`,
`fleet-token.mjs`, `issue-create.mjs`, `label-write.mjs`,
`sweep-stale-finding.mjs`, `write-pace.mjs`, `check-dispatch-gates.mjs`
| unrelated | numeric coincidences (fixture ids, timeouts, quota
numbers) |
### New pins
`check-governed-merges.mjs` — battery "⭐ the SIZE predicate": an
authorized approval lifts (exceeds stays true, `landsByHumanMerge`
false); threshold / strict comparison / inclusion unchanged; the words
print approver, commit, `objectstack#20153` and the seat landing; exit
is the NOT-governed code; no approval / `unapproved` / unauthorized /
`unreadable` / the generic reduction (no `approvals` field) each still
fire; an approval under the line lifts nothing; a governed PATH and a
FORK head are untouched by the lift; the sweep still lists an oversized
landing; the not-lifted words name both landings.
`check-governed-queue-guard.mjs` — battery "⛔ #19036: the SIZE line at
the queue": the governed leg's verdict object is REUSED (zero extra
reads, group exits 0); #20125 replay — approval on the head and on an
older commit both exit 0 with two reads; the block prints approver,
commit, card and ruling; no / unauthorized / dismissed / superseded → 8
with the reason named; unreadable review list → 8 never 9; no review
reader → 8; reviews are read only over the line (within PR: none;
unreadable size: still 9, none); end to end governed clear + size lifted
→ 0, and with no approval → 8; `sizeReading` has four states; the
authorized verdict carries `approvals` and the generic one does not; the
guard's source spells no lift condition of its own; the remedy names
both landings and keeps the bypass-rules option (#19344 battery
unchanged and green).
### Verification
Both self-tests green at head `52398a3b`: `check-governed-merges
--self-test` 454 assertions (was 441), `check-governed-queue-guard
--self-test` 292 cases (was 279).
Reverse verification (ablation, committed state,
`scripts/ablation-replace.mjs`, anchor hit 1 → 0, blob `4c5598c0d0c5` →
`c21c9c1a3338`, restored to the HEAD blob with `git diff HEAD` empty,
both legs): mutating the sibling's `sizeLimbFires` to ignore the lift
reddens the guard's self-test (6 of 292 cases fail: reuse, #20125
replay, block words, end to end, four states) and the sibling's own (3
failures). Direction: 转红, as predicted.
Gate list from `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `52398a3b` — 40 commands, all exit 0,
reconciled with `--ran` (40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN;
every line carries its exit code):
| command | exit |
|---|---|
| `node scripts/check-ci-filter-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs` (+ `--self-test`) | 0
/ 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `node scripts/check-declaration-mirrors.mjs` (+ `--self-test`) | 0 / 0
|
| `node scripts/check-scripts-symbol-anchors.mjs` (+ `--self-test`) | 0
/ 0 |
| `node scripts/check-self-test-wired.mjs` (+ `--self-test`) | 0 / 0 |
| `node scripts/check-self-test-workflow-commands.mjs` (+ `--self-test`)
| 0 / 0 |
| `node scripts/check-skills-token-ratchet.mjs` (+ `--self-test`) | 0 /
0 |
| `node scripts/check-whole-set-label-write.mjs` (+ `--self-test`) | 0 /
0 |
| `node scripts/pm/bare-root-worklist.mjs --self-test` | 0 |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 |
| `pnpm check:agent-test-spelling`, `check:bash32-floor`,
`check:cli-command-ids`, `check:closing-target-claim`,
`check:cross-package-test-inputs`, `check:declared-population-live`,
`check:docs-audit-scope`, `check:driver-memory-census`,
`check:entry-guard`, `check:gitlink-declared`, `check:nul-bytes`,
`check:parse-guard` | 0 each |
| `pnpm check:pm-dispatch-gates` (894 s) | 0 |
| `pnpm check:pm-governed-merges`, `check:pm-governed-prose`,
`check:pm-skill-id-lint`, `check:pm-skill-ratchet`,
`check:pnpm-filter-targets`, `check:ratchet-remedy-authority`,
`check:refd-timer-probe`, `check:required-contexts`,
`check:watch-hint-literal` | 0 each |
Line budget: `AGENTS.md` 1105 → 1106 (ceiling 1116, headroom 10);
`SKILL.md` and `landing-operations.md` unchanged (headroom 0 on both,
untouched). `check-skill-line-ratchet` green.
Changeset: none — the diff touches `scripts/pm/**` and `AGENTS.md` only,
nothing any released package ships; `skip-changeset`.
## Acceptance notes
- `check-governed-merges.mjs --pr 20125` could not be read from this
container (page 2 of the files walk answered HTTP 403 through the
env-token channel; the tool refused rather than answering on a subset).
Not a defect; the container's credential asymmetry.
- The sweep's `sizeCell` does not annotate an oversized landing with the
approval that lifted it (the card allows, does not require, that note);
the sweep reads `merged_by`, not reviews, so adding it would add a
review read per oversized row. Left as is.
- `check-skill-line-ratchet.mjs` carries a ceiling-ledger comment
narrating the 2026-09-18 ruling as "takes the same terminal"; it is
history of a count, not a rule statement, and is untouched.
## 维护者速读(草稿)
**改了什么**:队列守卫的「超 5000 行」这一腿,现在和受管路径(Tier H)一样,承认你(或 `GOVERNED_APPROVERS`
里的账号)的 APPROVED 审查:批准过就放行,由认领席走队列落地;你直接合并这条路不变。阈值
5000、严格大于、含生成物,一个都没动;fork PR 不受此影响;事后审计照样把超 5000 行的落地列出来。
**为什么改**:你 2026-09-27 的裁决「所以阈值写死成 5000 行 , 维护者已经批准了就是可以合并。」——#20125 已获
os-zhuang 批准仍被队列两次踢出,就是这条旧规则(「只认人工合并」)造成的。
**风险与代价(含回滚)**:代价是队列对每个超线的 PR 多读一次 review
列表(已被治理腿读过的直接复用,不重复读);批准后再推的提交不再被这一腿复审——与 Tier H 路径已接受的成本同形。review
列表读不到时按「未解除」拒收(退出码 8),不会误放。回滚 = revert 本 PR,两份脚本的自测各自变红,不会静默。
**席位意见**:(留空,由席位定稿)
**你要做的**:一个动作——本 PR 触及 `AGENTS.md`(Tier H),请 APPROVE 本
PR(或直接合并);批准后认领席走队列落地。
---
_Generated by [Claude
Code](https://claude.ai/code/session_0148fenvVvyQV9HYxgVDQ33q)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2bbebf5 commit 1f33392
4 files changed
Lines changed: 475 additions & 96 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
507 | 507 | | |
508 | 508 | | |
509 | 509 | | |
510 | | - | |
511 | | - | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
512 | 513 | | |
513 | 514 | | |
514 | 515 | | |
| |||
0 commit comments