Repository navigation
feat(pm): an enqueue by an authorized account is the human review at the queue - #26230
Conversation
Claude-Session: https://claude.ai/code/session_01XDSBMAmqzG7QYwCD7qAGty Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDSBMAmqzG7QYwCD7qAGty Co-authored-by: Claude <noreply@anthropic.com>
…the queue The governed queue guard's merge_group leg now passes a governed PR when EITHER an account in GOVERNED_APPROVERS holds a latest-decisive APPROVED review (unchanged, read first) OR that account put the entry in the queue, read as the actor of the newest added_to_merge_queue timeline event. An undetermined enqueuer is never a pass. The size limb follows the same predicate through the sibling's sizeLiftFrom. AGENTS.md Prime Directive 14 names the enqueue as part of the maintainer's hand. Claude-Session: https://claude.ai/code/session_01XDSBMAmqzG7QYwCD7qAGty Co-authored-by: Claude <noreply@anthropic.com>
…he line ratchet Claude-Session: https://claude.ai/code/session_01XDSBMAmqzG7QYwCD7qAGty Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewReviewed at 2026-10-26T16:38Z — PR #26230 at head Served-tier: ① Derived judgmentsCheck-runs on Every rule the diff adds, removes or narrows, judged against the order, the triage boundary and the guard's regime:
(a) Fail-closed — the code on the head, not the body. Stale adds and ordering: Attribution of an auto-merge-armed entry, re-measured. The body's fleet cases hold: #26154 Paging: (b) Identity floor. PD14 already carries "before that record no AI seat merges, queues, or arms auto-merge" and "⛔ never queue, ⛔ never arm auto-merge"; the new sentence 「An authorized account's enqueue is a review record too: ⛔ never a seat's.」 sits directly after the approving-review prohibition ("under any account — an authorized account is agent-operated too") and inherits "under any account" by construction, so the floor is stated where the rule lives. The guard header states it in the approval side's own words and names the exposure honestly: the prohibition "is the ONLY thing between an agent seat holding an authorized account and a governed landing it cleared for itself". On #11704: the new section quotes the founding ruling ( (c) Scope kept. PR leg: no timeline read, no (d) AGENTS.md. 1,117 lines on (e) Self-test and ablation. The battery ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
席位核对 — ACCEPT ·
|
Requested by Warren · project thread
Fixes #26066
Clause-②: no
Metadata-protocol: none
What changed
The governed queue guard's
merge_groupleg now passes a governed pull request when EITHER an account inGOVERNED_APPROVERSholds a latest-decisive APPROVED review (the 2026-09-04 predicate, unchanged and read first) OR that account put the entry in the queue. The maintainer's own "Merge when ready" fromos-zhuangorhotlongis the human review; no separate Approve is owed. The approver set is unchanged and is still the single constant.scripts/pm/check-governed-queue-guard.mjsenqueuerFromTimeline/authorizedEnqueueVerdict/undeterminedEnqueue(pure) andmakeTimelineReader(pagedGET /issues/N/timeline, throws on truncation because the newest event is on the last page).runGuardreads the enqueuer SECOND, only for an entry the approval did not satisfy (monotone, free for approved entries), before any Tier S record read.entrySatisfiedgains the enqueue limb between the approval and the record;satisfiedBynames the leg.✅ enqueued by an authorized account: LOGIN (added_to_merge_queue at T), a third CLEARED wording ("The ENQUEUE leg satisfied N of them: #N (enqueued by LOGIN)") so the clear never claims an approval it did not read, and on refusal⛔ enqueued by LOGIN, outside GOVERNED_APPROVERSor⛔ enqueuer UNDETERMINED — reason.runSizeGuardhands the enqueue reading beside the approval verdict to the sibling's lift; a governed and oversized PR costs one timeline read, not two. Remedies on both legs name the enqueue landing; the bypass-rules option stays named.completed4 minutes after filing with both items unanswered #11704 retracted, the accepted cost, and the exit-table rows for 0 / 3 / 8.scripts/pm/check-governed-merges.mjs(claim amendment, those two functions and their pins only):sizeLiftFromreads anauthorizedenqueue as a second lift after the approval lift;renderLiftApprovalsrenders it; newSIZE_ENQUEUE_LIFT_RULING.AGENTS.md, inside its line ratchet (1,117 lines before and after, no line over 120 bytes): Prime Directive 14 says Tier H lands "by the maintainer's hand (merge or enqueue) or an authorized approval"; one sentence adds "An authorized account's enqueue is a review record too: ⛔ never a seat's."; and Multi-agent §7 (c) says the size line lands by "the maintainer's merge or enqueue". The details (for that queue entry only, undetermined never passes) are in the guard header, which is where the rule's detail lives.The enqueuer reading, measured (card §2)
Measured on 2026-10-26 against the live REST API of this repository:
added_to_merge_queuetimeline event'sactor.loginanswers both paths. feat(spec)!: refuse an SLA escalateTo that no approver acts under, at registration #26154 and feat(spec,console): atype: 'flow'action may declareonSuccess, and the console navigates after a run that completes #26011: armed through auto-merge, and the add event's actor is the account that armed it. fix(app-shell): draw the app no-access screen with the shared DataEmptyState #26166: direct enqueue, add event present, and noauto_merge_enabledevent at all.auto_merge.enabled_by.loginis null on a direct enqueue (fix(app-shell): draw the app no-access screen with the shared DataEmptyState #26166 never had one) and on every merged PR read, so it answers only the armed path. Not used.GET /issues/N/timelineanswersX-Accepted-GitHub-Permissions: issues=read; pull_requests=read. The workflow's existingpull-requests: readcovers it, so.github/workflows/governed-surface-guard.ymlis untouched.merge_grouprun's own token reading the timeline, and an enqueue armed from an authorized account reaching the queue (feat(spec,cli)!: retire the ≤16.x retirement kits; the strict unknown-key refusal names the protocol upgrade guide #26138 was armed byos-zhuangand not yet queued at read time). The first live queue build after this lands prints the reading either way, and an unreadable one is UNDETERMINED, which is today's behaviour.UNDETERMINED is never a pass: no add event, an actorless add, an unreadable or truncated timeline, and a newest queue event that is a removal (the current entry is not on the timeline yet, so the earlier add belongs to an entry that left) all leave the approval leg to decide alone, on its own exit code (3, not 4).
Tests
At head
9e0ae37b(the last commit touched onlyAGENTS.md; both self-tests re-run there):node scripts/pm/check-governed-queue-guard.mjs --self-test: 373 cases pass (was 345). New battery "the 2026-10-26 ENQUEUE leg" with 28 cases, floor 27 to 28. It covers the card's four fixtures (authorized enqueue with no review passes and names the leg; seat enqueue with no review is refused on 3 with today's words; seat enqueue with an authorized approval passes on the review leg and never reads the timeline; unreadable enqueuer with no review is refused on 3 naming UNDETERMINED). It also covers a stale add before a removal, no reader, an unreadable review list beside an authorized or a seat enqueue (0 and 4), the PR leg reading nothing, a Tier S entry clearing without a record read, the size lift and its refusal,main()'s wiring at one timeline read, and the reader's paging, truncation and non-2xx paths.node scripts/pm/check-governed-merges.mjs --self-test: 537 assertions pass, including 3 new enqueue-lift pins.entrySatisfiedmutated (scripts/ablation-replace.mjs, blobecad10abe0f0toe05e95b1cbb5): 5 of 373 cases red (fixture 1, its words, the unreadable-review case, the Tier S case, the governed-and-oversized case). Restored, blob equals HEAD,git diff HEADempty.enqueuerFromTimelinemutated: the stale-add case red (1 of 373). Restored, blob equals HEAD. The first attempt hit an anchor miss (wrong indentation), wrote nothing, and was re-run on the right anchor.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; 3 changed paths vs merge basefbb75998b): 42 families.9e0ae37b: 41 of 42 run locally, all exit 0 (exit captured before any pipe), includingnode scripts/pm/check-governed-queue-guard.mjs --self-test,pnpm check:pm-governed-prose,pnpm check:pm-governed-merges,pnpm check:pm-skill-ratchet,pnpm check:pm-skill-id-lint,pnpm check:required-contexts,pnpm check:nul-bytes.--ranreconciliation: 42 derived, 41 run, 0 NOT-MEASURED, 1 UNRUN.pnpm check:pm-dispatch-gates. It ran at8258383f(the head before the AGENTS.md-only rewrap commit) and exited 0: fast tier, 1917 cases, 618 s. It was not re-run on9e0ae37b; CI runs it.8258383fhad one red,pnpm check:pm-skill-ratchet: AGENTS.md was over its 1,117-line ceiling (1,121) and had two lines over 120 bytes. The sentences were rewritten to fit the existing lines (no ceiling raised, no unrelated re-wrap). It is green at9e0ae37b.pnpm exec eslint --no-inline-config) on the two changed scripts: exit 0. Repo-widepnpm lintis left to CI.Acceptance notes
pnpm check:governed-queue-guard-self-test. No such script exists inpackage.json; the guard's--self-testis in the derived list and was run directly. Repo-widepnpm lintis CI's per.claude/agents/os-dev.md; ESLint ran locally on the two changed scripts (exit 0).AGENTS.md). This PR stays draft and lands by the maintainer's hand. Chicken-and-egg: until this change is inmain, that hand is an APPROVED review or the bypass-rules merge, not the enqueue.维护者速读(草稿)
改了什么:合并队列守卫多认一种"人工审核":PR 若是由授权账号(os-zhuang 或 hotlong)点"Merge when ready"送进队列的,队列守卫直接放行,不再要求先点 Approve。体积超 3000 行的 PR 同样适用。其他账号(包括车队机器人)入队仍然必须有授权 Approve。
为什么改:您在手机上原来要先点核准再点合并;按您的要求("大部分我还是希望入队,但是我想省掉 Approve"),以后直接点合并(入队)即可。
风险与代价(含回滚):守卫读的是 PR 时间线上最新一次"入队"事件的操作账号。① 授权账号若被智能体代操作,入队就等于放行,所以 AGENTS.md 同步写明:任何智能体席位都不得以任何账号在未获批的受管 PR 上入队或开自动合并。② 入队后到落地前再推的提交不会被重新审核,与"批准在任意提交上都算"的代价相同。③ 读不到入队者时不放行,仍按原规则判。回滚:还原本 PR 即可,无数据迁移。
席位意见:
你要做的:本 PR 改了 AGENTS.md,属 Tier H,需要您亲手落地:点 Approve 后由席位落地,或直接合并。新规则要等本 PR 进 main 后才生效,所以这一次还不能只靠入队。
Generated by Claude Code