Repository navigation
feat(pm): the relay requests review from every GOVERNED_APPROVERS account on a Tier H pull; CODEOWNERS routes every Tier H surface to both approvers - #25064
Conversation
…ount on a Tier H pull, and CODEOWNERS routes every Tier H surface to both A seat-driven Tier H pull is opened draft and stays draft until the approval, and GitHub requests a code owner only at ready_for_review (measured on this repository's own Tier H pulls: the CODEOWNERS request arrived one second after the ready flip, never at the draft create). CODEOWNERS listed docs/adr/ -> @hotlong alone, and SKILL.md's "request review from both approvers" was prose no op performed. So an approver waited on a pull nobody told him about. - fleet-write/ops.mjs: pr_create and pr_ready carry governedReview; governedReviewRequests spells the two conditional writes (requested_reviewers, issue-assignees); pr_ready spends issues for the assignee fallback. - fleet-write/execute.mjs: after a governedReview row lands, read the pull's whole file list (every page, a cap that reds as a truncated subset), classify through the register (governedPathsIn + landingTierOf, scoped to payload.repo), and on a Tier H hit request every GOVERNED_APPROVERS account not already requested, assigned or holding a submitted review; the pull's own author is assigned instead (a request naming it is a 422). Tier S, ungoverned and covered pulls send nothing and say so; an unreadable list, pull or review list, a truncated walk or a refused write is a FAILED action, never a quiet skip. Self-test: a new battery of 29 cases; fixtures of the pr ops now declare the files read. - fleet-write/validate.mjs: pins the two rows, the two endpoints against the refused families, and the three alsoSpends rows. - .github/CODEOWNERS: every Tier H register row and the enforcement chain route to @os-zhuang @hotlong; the header names GOVERNED_APPROVERS as the list it mirrors. - check-governed-queue-guard.mjs: codeownersMirrorVerdict reads the file and the self-test reds when a Tier H row is missing, an owner set drifts from the constant, or a Tier S row is routed (a pin inside an existing self-test, no new gate). - pm-dispatch SKILL.md step 3 and rest-channel.md point at the op (net 0 lines each). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M82feuetLzrCiBz41BJb7L
Contract reviewServed-tier: Inputs: card #25046 (body, the Claim 6308718151, the os-dev report 6309589418); PR #25064 body, file list (7 files, +525/−44) and the three-dot diff at the head; the head's check-runs; ① Derived judgmentsA. The relay's governed-review follow-up (
B.
C. The mirror pin (
D. The rules text (SKILL.md ③, rest-channel.md) — net 0 lines each (319 → 319, 82 → 82); every edited line within the 120-byte budget (105, 119, 118, 69; 120).
② Semver level
Check-runs on the head, read 2026-10-21T06:36Z: 30 success, 13 skipped by the paths filter (Build Core, Build Docs, Console Pin Gate, the dogfood shards, the opt-in harnesses), ONE in progress: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读改了什么:
为什么改:你说「申请我批准的应该推送给我,我看不到任何提示」,原因有两个:
现在请审由工具执行,不再靠席位记得去做。 风险与代价(含回滚):
席位意见:建议批准。
你要做的:在 PR #25064 上点 Approve,席位随后落地。 席位核对ACCEPT ·
Generated by Claude Code |
Fixes #25046
Clause-②: no
维护者速读(草稿)
改了什么
.github/CODEOWNERS:六个 Tier H 受管面(docs/adr/、skills/、AGENTS.md、CLAUDE.md、docs/NORTH-STAR.md、.github/workflows/)与三条执法链行(本文件、check-governed-merges.mjs、check-governed-queue-guard.mjs)全部路由到@os-zhuang @hotlong;文件头写明 owner 列表镜像GOVERNED_APPROVERS常量。fleet-write:pr_create(开 draft PR)与pr_ready(转正式)落地后,执行器读 PR 完整文件清单、按注册表判级;命中 Tier H 就向GOVERNED_APPROVERS里尚未被请审、指派或已评审的每个账户发 review request;PR 作者本人是批准账户时改为 assign(GitHub 对"请审作者"回 422)。Tier S、未受管、已覆盖的 PR 零写并在 run 摘要说明;文件清单读不到或被截断、写入被拒,都是 FAILED action,绝不静默跳过。check-governed-queue-guard.mjs --self-test新增 12 条用例:读磁盘上的 CODEOWNERS,与注册表 Tier H 行、GOVERNED_APPROVERS常量比对,漂移即红。不新增门禁、check-run 或巡检行。SKILL.md第 ③ 条与rest-channel.md改为指向该 op(各净 0 行)。为什么改
hotlong的请审 11:22:14 才到,紧跟ready_for_review一秒之后)。所以 draft 阶段没有任何机制通知批准人;原 CODEOWNERS 又只写了docs/adr/到@hotlong,os-zhuang从未被叫到。「应该是skills有问题,申请我批准的应该推送给我,我看不到任何提示」—— 请审现在是一个 op,不再是一句散文。风险与代价(含回滚)
GOVERNED_APPROVERS必须同一 PR 改 CODEOWNERS,否则守卫自检红。席位意见
(留空)
你要做的
.github/CODEOWNERS自身行;AGENTS.md 所列受管面的路由规则由本文件承载)。获批后席位落地。What this PR does
A seat-driven Tier H pull is opened draft and stays draft until the approval, and GitHub requests a code owner only at
ready_for_review. CODEOWNERS listeddocs/adr/to@hotlongalone, and SKILL.md's "request review from both approvers" was prose no op performed. Three things change:.github/CODEOWNERSroutes every Tier H register row (/docs/adr/,/skills/,/AGENTS.md,/CLAUDE.md,/docs/NORTH-STAR.md,/.github/workflows/) and the enforcement chain (itself,check-governed-merges.mjs,check-governed-queue-guard.mjs) to@os-zhuang @hotlong; its header namesGOVERNED_APPROVERSas the list it mirrors.GET /repos/objectstack-ai/objectstack/codeowners/errors?ref=claude/issue-25046-tier-h-review-requestanswers{"errors":[]}(read 2026-10-21 after the push), so both rows are live — a code owner without write access would be an error there.fleet-write/ops.mjsmarkspr_createandpr_readywithgovernedReview: trueand spells the two conditional writes ingovernedReviewRequests. After the row's own requests land,execute.mjsreads the pull's whole file list (every page; a walk past the endpoint's 30-page ceiling is a FAILED action, never "nothing governed"), classifies through the register (governedPathsIn+landingTierOf, scoped topayload.repobyrepoIdForSlug), and on a Tier H hit requests review from everyGOVERNED_APPROVERSaccount not already requested, assigned or holding a submitted review — the union the H43 patrol reads as covered. The pull's own author is never requested (GitHub answers 422); a missing approver who is the author is assigned instead. Each answer is judged like the assignees leg: a 2xx lacking a login sent is a FAILED action naming it. Tier S (.claude/**alone), ungoverned and covered pulls send nothing and the summary row says which.check-governed-queue-guard.mjsgainscodeownersMirrorVerdict, and its--self-testreads.github/CODEOWNERSfrom disk: a Tier H row with no line, any line whose owner set differs from the constant, or a routed Tier S row reds it. No new gate, check-run or patrol row.Landing site for the op: the relay executor (candidate b). Of the three homes the card named, only the executor knows the pull number at the right moment (the create's answer), holds the token, and runs on the board's
maincheckout where the register lives, so the Tier H decision is the board's single source rather than a seat checkout that may be behind. The seat client (a) would have to compute the diff from a compare and could not name a number that does not exist yet; the landing helper (c) does not exist as a script (the landing stroke is an actions file) and its moment — after the approval — is too late for Tier H.Measured — the five hypotheses
origin/main63085c380had three rows, all@hotlong. The Tier H row set is derived fromGOVERNED_SURFACES(governedSurfacesFor('objectstack'), tier H) and spelled as root-anchored prefix/exact patterns; the self-test pins the on-disk file against that derivation and againstGOVERNED_APPROVERS, so the two stay in step by a pin, not by hand. The merges self-test's classification of.github/CODEOWNERSas a near-miss (not a governed row) is unchanged.pnpm install) loads them;validate.mjsalready importedcheck-governed-merges.mjson the runner.pr_createandpr_ready. The timeline of PR 24859 shows the draft create at 2026-10-20T11:19:43Z with noreview_requested, thenready_for_reviewat 11:22:13Z and CODEOWNERS'review_requested hotlongat 11:22:14Z. So the draft open is the moment the platform is silent, and the op must fire there. It also fires atpr_ready, because the file list may have grown a Tier H path since the open and the follow-up is idempotent (zero writes when covered), so the second firing costs one read.objectstack-fleet[bot]-authored pull requests both approvers; a pull authored by an approver requests the other and assigns the author.requested_reviewers, itsassigneesand the authors of its submitted reviews (GET …/pulls/{n}/reviews, any state but PENDING) before sending, so a re-run, apr_readyafter apr_create, or the platform's own CODEOWNERS request landing first costs zero writes. No throwaway PR was opened.Tests
node scripts/pm/fleet-write/execute.mjs --self-test→✓ fleet-write/execute self-test: 167 cases pass across 18 batteries(new battery of 29 cases; floor 17 → 18).node scripts/pm/fleet-write/validate.mjs --self-test→✓ fleet-write/validate self-test: 160 cases pass across 11 batteries(the refused-family pin now covers the two follow-up endpoints; the alsoSpends pin names three rows).node scripts/pm/fleet-write/dispatch.mjs --self-test→✓ fleet-write/dispatch self-test: 235 cases pass across 21 batteries(unchanged file).node scripts/pm/check-governed-queue-guard.mjs --self-test→✓ check-governed-queue-guard self-test: 334 cases pass(new battery of 12 cases; floor 26 → 27).scripts/ablation-replace.mjs): anchorfollowed = await requestGovernedReview(…)→followed = { ok: true }; tool evidenceanchor x1 -> x0,blob c660b170c9e0 -> 58e7fdeed4fd; the executor self-test went red17 of 167 case(s) failed, including⭐ pr_create on a Tier H diff: … ONE requested_reviewers POST naming BOTH approvers; restore verifiedblob == HEAD (c660b170c9e0) and git diff HEAD is empty./docs/adr/ @os-zhuang @hotlong→/docs/adr/ @hotlongin.github/CODEOWNERS;blob 15d3fffd682e -> f223af300219; the guard self-test went red2 of 334 case(s) failed(the-file-on-disk-mirrors-the-register-and-the-approver-list: CODEOWNERS line 42 /docs/adr/ names @hotlong, not every GOVERNED_APPROVERS account); restore verifiedblob == HEAD (15d3fffd682e) and git diff HEAD is empty.Gates (local subset, at head
91669b139)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; changeset from the merge base) derived 60 families on the actual diff — the dispatch lead's 58 pluspnpm check:closing-target-claimandpnpm check:ratchet-remedy-authority. Every one was run with the capture idiom and reconciled:✓ dispatch-gates --ran: 60 derived famil(ies) accounted for — 59 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3). The NOT MEASURED one ispnpm --filter @objectstack/lint run check:doc-formula-expressions(exit 3, PREREQUISITE NOT MET: it wants a@objectstack/formula+@objectstack/lintbuild; this diff touches no package, so no build closure was made — CI runs it). Among the 59 green:check:pm-fleet-write-{dispatch,execute,validate},check:pm-governed-merges,check:pm-governed-prose,check:pm-skill-ratchet(SKILL.md 319/319, rest-channel.md 82/82, every edited line under the 120-byte line budget),check:pm-skill-id-lint,check:skill-frame-sync,check:pm-dispatch-gates(fast tier, 1913 cases, 328.7 s),check:pm-write-pace,check:doc-authoring,check:required-contexts,node scripts/check-self-test-wired.mjsand its--self-test,node scripts/pm/check-governed-queue-guard.mjs --self-test. The families CI runs on the PR are read from its check-runs by the seat; none was re-run locally beyond this subset.Acceptance notes
docs/adr/only, so nothing summons the maintainer on the other Tier H surfaces". After this PR that sentence is stale. AGENTS.md is outside this card's file surface (Tier H, its own line ratchet), so it is not edited here; a one-line rewrite is the follow-up.check:pm-governed-prosedoes not pin that sentence (verified green here).pr_ready+automerge_enablelanding stroke now performs one extra read (the readied pull's files) and, on a Tier H pull, up to two paced writes;strokePacingdoes not count them (conditional, at most two per pull), so they ride rule ①'s gap — documented in the ops.mjs header.pr_createwhose file list cannot be read is now a FAILED action (exit 5 for the dev's dispatch) with the pull already open; the row and the log say what was not sent. This is the loud direction on purpose: silence here is the defect.pr_creategoes through the relay executor onmain, which predates this change, so this pull requests nobody by itself; the seat requests review after its at-tier contract review, per the dispatch order.check-governed-merges.mjs --self-testkeeps pinning.github/CODEOWNERSas a near-miss that is NOT a governed row; CODEOWNERS routes its own edits instead. Not changed, and I do not think it is wrong: the file is an enforcer, and the register is the audit's surface list.Out-of-scope findings
domain:skillsseat, as a one-line follow-up on the Tier H file. Dedupe words: CODEOWNERS, docs/adr only, Prime Directive 14, summons.Generated by Claude Code