diff --git a/AGENTS.md b/AGENTS.md index b005998de..93288d1e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ Grok Bot is the single entry point for humans; it schedules OpenCode (implements 3. **Verify locally:** `npm run verify` at the repo root (Node 22+, see Commands). 4. **PR** to `main`. Imperative English title, `Fixes #N` in the body, and what you ran and did **not** run at the tip SHA. A user-visible change adds one fragment `changelog.d/..md` ([changelog.d/README.md](changelog.d/README.md)); never edit `CHANGELOG.md` in a feature PR. Squash is the only merge method; the PR title and body become the commit message, so keep the body short. 5. **CI.** The required check is `tests-ok`. It is green only when `test`, `plugins (tree)`, `plugins (social)` and `package` all pass. Never merge around a red check. -6. **Review and merge.** Merge by tier ([`risk-gate.mjs`](.github/scripts/risk-gate.mjs): `node .github/scripts/risk-gate.mjs --pr `). **T0 and T1** (owner decision 2026-10-19 11:45): an agent may enqueue the PR once `tests-ok` is green at its tip, it is mergeable, and it conforms to the design rules. No PASS is needed. **T2 and T3** need a PASS at the tip SHA from [`review-pr`](.claude/skills/review-pr/SKILL.md) (PASS / FAIL / INC) with green checks, and then **the agent that gated the PR enqueues it itself**: since 2026-10-21 the owner's authorisation is the [self-gate](#owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized), not a click in the UI. **Self-review counts** (owner decision 2026-10-19 11:43): a PR opened by the owner's Bot/agent (the `Birfy` account) may be PASSed by the owner's own reviewer Bot/agent; no independent reviewer is required. A tip move (any new commit) voids the PASS. Neither the PASS nor the self-PASS replaces green CI. Enqueue with `gh pr merge --auto --squash --match-head-commit `, and run `node .github/scripts/enqueue-guard.mjs --pr ` immediately before it: the guard re-reads the live head and refuses (exit 1) on a `hold`, on a newest conclusion that is not a PASS, and on a report marked "record only — do not merge", so a PASS is never a queue permit on its own (#602). `main` requires a merge queue: that command enqueues the PR (gh may answer `The merge strategy for main is set by the merge queue` or `already queued`), the queue performs the squash merge, and a plain `--squash` merge is refused; do not retry ([merge plumbing](docs/autonomous-dev.md#merge-plumbing-merge_group-merge-queue-auto-merge)). Never admin-merge and never bypass a gate. Exception: a mechanical release PR under the [standing approval](#standing-approval-mechanical-release-prs) below. [`fix-issue`](.claude/skills/fix-issue/SKILL.md) walks all of this, with the environment gotchas. +6. **Review and merge.** Merge by tier ([`risk-gate.mjs`](.github/scripts/risk-gate.mjs): `node .github/scripts/risk-gate.mjs --pr `). **T0 and T1** (owner decision 2026-10-19 11:45): an agent may enqueue the PR once `tests-ok` is green at its tip, it is mergeable, and it conforms to the design rules. No PASS is needed. **T2 and T3** need a PASS at the tip SHA from [`review-pr`](.claude/skills/review-pr/SKILL.md) (PASS / FAIL / INC) with green checks, and then **the agent that gated the PR enqueues it itself**: since 2026-10-21 the owner's authorisation is the [self-gate](#owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized), not a click in the UI. **An implementer does not review its own pull request** ([Merge gates](#merge-gates)): the PASS comes from the reviewer Bot, and because every Bot shares the `Birfy` account, a PASS by the owner's reviewer Bot on a PR another Bot opened still counts (owner decision 2026-10-19 11:43). A tip move (any new commit) voids the PASS. Neither the PASS nor green CI replaces the other. Enqueue with `gh pr merge --auto --squash --match-head-commit `, and run `node .github/scripts/enqueue-guard.mjs --pr ` immediately before it: the guard re-reads the live head and refuses (exit 1) on a `hold`, on a newest conclusion that is not a PASS, and on a report marked "record only — do not merge", so a PASS is never a queue permit on its own (#602). `main` requires a merge queue: that command enqueues the PR (gh may answer `The merge strategy for main is set by the merge queue` or `already queued`), the queue performs the squash merge, and a plain `--squash` merge is refused; do not retry ([merge plumbing](docs/autonomous-dev.md#merge-plumbing-merge_group-merge-queue-auto-merge)). Never admin-merge and never bypass a gate. Exception: a mechanical release PR under the [standing approval](#standing-approval-mechanical-release-prs) below. [`fix-issue`](.claude/skills/fix-issue/SKILL.md) walks all of this, with the environment gotchas. 7. **Stop** and hand over to the maintainer on a security-sensitive change, an unresolved maintainer decision, or when you cannot get a green tip. When a class of bug repeats, date a lesson in LESSONS.md (`Seen N×`); after **≥3 days** promote the durable rule into `docs/code-standards.md` / CONTRIBUTING. @@ -94,7 +94,7 @@ Decided by the owner (Danyang, GitHub `Birfy`) on 2026-10-18/19. Four scheduled - **Type** (exactly one): `bug`, `enhancement`, `documentation`, `accessibility`. - **Area** (one or more): `area:social`, `area:ui`, `area:automation`, `area:release`, `area:security`, `area:docs`. - **Source:** `self-test`. -- **State:** `needs-info` (spec unclear), `needs-human` (needs a design decision from the owner; the comment says what), `awaiting-approval` (legacy: a PR a person must decide on; routine T2/T3 PRs no longer wait for a click), `agent-ready`, `agent-working`, `agent-pr`, `hold`. +- **State:** `needs-info` (spec unclear), `needs-human` (needs a design decision from the owner; the comment says what), `agent-ready`, `agent-working`, `agent-pr`, `hold`. - **Controller-managed, never set by hand:** `risk:T0` to `risk:T3`, `agent-task`, `status:done`. - **A `risk:T*` label is advisory, and never the tier a decision uses.** The controller is a poll: GitHub delays scheduled runs by hours (2.5–6 h spacing observed on this repository, [#629](https://github.com/ThinkFlowLab/FlyBot/issues/629)), and a fork PR's `pull_request` run has a read-only token, so it cannot label one at all. An open PR can therefore carry no tier until the next run, and a PR opened and merged between two runs never gets one. Read a missing label as "not polled yet" — never as T0/T1 and never as a reason to block. The tier that decides is the one `merge-gate` recomputes from the diff at merge time, and `risk-tier.yml` reports the same tier on every PR. - **The computed tier is readable from the job summary.** `risk-tier.yml` ends its summary with one machine-readable line — `risk-tier: T3; basis: AGENTS.md=T3, …` — built by the same `judge()` call that prints the report, so the line and the prose cannot disagree. Quote that line instead of guessing a tier. The workflow itself never writes a label, and no review transcribes a tier onto a PR: the controller copies it on its next poll (#609). @@ -131,21 +131,28 @@ What this buys is presence and a name from a closed list, not truth: with one sh ### Gates - **T0 / T1:** merge through the merge queue once `tests-ok` is green at the tip, the PR is mergeable and design-conformant. No PASS is required. -- **T2 / T3:** a PASS at the head commit from a reviewer in [`.github/agent/review-policy.json`](.github/agent/review-policy.json), with green CI, and the agent that gated it then enqueues it itself ([self-gate](#owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized)). **Self-PASS counts:** a PR opened by the owner's Bot/agent (`Birfy`) may be PASSed by the owner's own reviewer Bot/agent (the policy's `selfReview` list); an independent reviewer is no longer required. Neither the PASS nor the self-PASS replaces green CI. +- **T2 / T3:** a PASS at the head commit from a reviewer in [`.github/agent/review-policy.json`](.github/agent/review-policy.json), with green CI, and the agent that gated it then enqueues it itself ([self-gate](#owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized)). **T2 and T3 need no maintainer approve** (user decision 2026-10-22): no human `APPROVED` review is read or required, and `merge-gate` re-judges the head on the queue entry. **Self-PASS across Bots counts:** a PR opened by the owner's Bot/agent (`Birfy`) may be PASSed by the owner's own reviewer Bot/agent (the policy's `selfReview` list) — the shared account is why that list exists, not a licence for an implementer to review its own pull request. Neither the PASS nor green CI replaces the other. - **A PASS must list what it ran.** The `Ran:` line (or a `Ran …:` heading over `$ ` commands) carrying at least one real command is part of the gate, not a nicety: `merge-gate` refuses a PASS that lists none (#681, #574). When a PASS at a tip cannot be read for that reason, the reviewer may post **one replacement PASS at the same tip**: its body keeps the original conclusion, adds the `Ran:` lines of what was actually run, and carries `Replaces: the PASS at merge-gate rejected for having no Ran: line`. That is a replacement, not a second conclusion — apart from it, one tip still carries one. - Any new commit voids a PASS. CI green, the merge queue and the no-bypass rule apply to every tier. - Agents never bypass a gate and never admin-merge (the routines run as the maintainer account `Birfy`, but use it only for the actions above, never for admin merge, bypass or changing protection rules). The merge queue squashes the PR title and body, so a `Docs-not-needed:` line goes in the PR **body** and also in a branch commit message (`npm run arch-docs` reads only the branch's commits). Run `verify` with `TZ=UTC`. ### Merge gates -Four rules decide whether a pull request may enter the queue. The mechanical one runs where the merge actually happens: [`merge-gate.yml`](.github/workflows/merge-gate.yml) fails the `merge-gate` job on a `merge_group` entry, and [`merge-gate.mjs`](.github/scripts/merge-gate.mjs) does the judging. +**T2 and T3** take one path into `main`: a PASS at the tip that lists the commands it ran, every required check green at that tip, and `merge-gate` re-judging the pull request head when the entry reaches the queue. **T0 and T1** take the same path without the PASS. The queue is the only route in — `merge-gate` is a required check of the `main merge queue` ruleset, so a ref push that never built a queue entry cannot land. -1. **`hold` blocks mechanically.** A pull request labelled `hold` fails `merge-gate` on its queue entry. The label is only a marker; the job is the gate. It blocks the queue once `merge-gate` is listed among the required checks of the `main merge queue` ruleset — a ruleset change for the maintainer, never an agent; until then the job reports without blocking. +- **T2 and T3 need no maintainer approve** (user decision 2026-10-22 07:15Z). A human `APPROVED` review is not one of the gates and nothing reads one; the tier decides how closely a change is reviewed, not who signs it off. +- **No implementer writes the Verdict.** On the API side self-review cannot be told apart mechanically — every pull request is opened by the shared `Birfy` account, and `selfReview: ["Birfy"]` in [`review-policy.json`](.github/agent/review-policy.json) makes a PASS always look like Birfy judging Birfy — so what the team holds is the role split: the implementer Bot does not write the Verdict, the Bot that writes the Verdict neither changes the tip nor re-judges its own verdict, and `selfReview` stays as it is because clearing it would leave every pull request without a conclusion. +- **`hold` is human-only.** Only a person sets the label and only a person lifts it; automation refuses a held pull request and never removes the label itself. That is the one human input left on the merge path. +- **The label awaiting-approval is retired.** A PASS at the tip goes straight to the queue through the enqueue guard (#687); no pull request waits for an approval click and the label is not set again. + +Four rules decide what a queue entry must pass. The mechanical judge runs where the merge actually happens: [`merge-gate.yml`](.github/workflows/merge-gate.yml) fails the `merge-gate` job on a `merge_group` entry, and [`merge-gate.mjs`](.github/scripts/merge-gate.mjs) does the judging. + +1. **`hold` blocks mechanically.** A pull request labelled `hold` fails `merge-gate` on its queue entry. The label is only a marker; the job is the gate, and it is a required check of the `main merge queue` ruleset (#639, set 2026-10-22), so the refusal holds the entry. 2. **Labelling a pull request `hold` means adding a record.** Add the label **and** post a new comment saying **Record only — do not merge / 仅记录、禁止入队**. The pair is what holds it. Add that comment; never edit or delete an earlier PASS report to change what it said — the history is the record, and rewriting it is the thing that reads as an incident afterwards. 3. **An authorisation is not a queue permit.** The owner or the Chief saying "let it through", "claim it" or "handle it along with the rest" licenses no specific pull request for the queue. Entering the queue is an explicit action on that one pull request, after its own gate passed, while it carries no `hold`. #566 is the case this exists for: "handle it along with the rest" was read as a permit, the tip was PASSed at 09:48:18Z, enqueued at 09:48:28Z and merged at 09:50:40Z as `abaf938b`. 4. **The newest conclusion at the head being merged decides.** Read the pull request's comments and review bodies, live, for the exact head SHA being merged — the pull request's own head, on a queue entry too (the `` in `gh-readonly-queue/main/pr--` is the entry's base, not its head; #654) — and take the newest text that carries `Verdict:` or a `## Review —` heading: INC or REQUEST_CHANGES refuses, PASS allows, no conclusion refuses too, and so does a report with no `Written-by:` line or one that names a writer the policy does not list (#627). Since #637 the format is fixed: the **first line** of the conclusion is exactly `Verdict: PASS at ` / `Verdict: INC at ` / `Verdict: REQUEST_CHANGES at `, with the attribution on the second line (`Written-by: Reviewer`, and a relay adds `Posted-by: Chief (proxy…)`; the older prose `Written by: …` reads the same way) and the evidence below it — a body that carries a verdict anywhere else is no conclusion, and no conclusion refuses. A result from `pull_request` time never stands in — on #610 the tip report ran at 10:31:08Z and the INC landed at 11:24:37Z, 54 minutes later. The gating agent checks this before enqueueing; `merge-gate.yml` checks it again at `merge_group`, and the stricter answer wins. That job runs on two events: at `merge_group` it judges and a refusal fails it, and at `pull_request` it only reports what the queue entry would refuse — a required check that never reports on the pull request head leaves every pull request waiting for a status that never comes and unable to enter the queue at all (#639). The required status check is the job name `merge-gate`; the workflow name `Merge gate` is not a context any run produces. -A review's `state` is not what is read: the account cannot approve its own pull requests, so its reviews stay `COMMENTED` even for an INC, and the INC on #610 was a comment rather than a review. +A review's `state` is not read at all: approvals are not a gate any more (user decision 2026-10-22), and the account cannot approve its own pull requests, so its reviews stay `COMMENTED` even for an INC — the INC on #610 was a comment rather than a review. **The T0/T1 exemption is computed, never labelled.** `merge-gate.mjs` runs the base copy of [`risk-gate.mjs`](.github/scripts/risk-gate.mjs) over the changed files and `.github/agent/risk-rules.json` to get the tier; a `risk:T0`/`risk:T1` label does nothing. Every Bot shares one account and a label carries no signature (#627), so a label-based exemption would let whoever can label turn a T3 into "no review needed" — the gate itself. A mechanical release PR is recognised by its files as before, and `hold` still refuses whatever the tier. diff --git a/docs/autonomous-dev.md b/docs/autonomous-dev.md index 69e597c6c..44154960b 100644 --- a/docs/autonomous-dev.md +++ b/docs/autonomous-dev.md @@ -17,8 +17,8 @@ Decisions by the maintainer, and what follows from them: 3. **Agents act only on issues labeled `agent-ready`**. That label is set by a maintainer, or by the owner's maintenance routine after triage (the routine runs as the maintainer account `Birfy`, so the controller sees a maintainer's label). No LLM develops other issues, and nothing outside text sets the label. -4. **Merge by tier** (owner decisions 2026-10-19, revised 2026-10-21): **T0 and T1** merge through the merge queue once CI is green, the PR is - mergeable and it conforms to the design rules, with no PASS. **T2 and T3** need a PASS at the tip (a **self-PASS counts**, below) with green CI, and then the agent that gated the PR enqueues it itself: the owner's authorisation is the self-gate, not a click in the UI (owner decision 2026-10-21, AGENTS.md). The gate is the review: green `tests-ok` at the tip, a PASS at that same tip, and a test that fails when the change is reverted. Work that meets neither the defect nor the routine-need bar gets no PR at all (AGENTS.md). An issue that already has a +4. **Merge by tier** (owner decisions 2026-10-19, revised 2026-10-22): **T0 and T1** merge through the merge queue once CI is green, the PR is + mergeable and it conforms to the design rules, with no PASS. **T2 and T3** need a PASS at the tip from the Reviewer Bot and green CI, and then the agent that gated the PR enqueues it itself — **T2 and T3 need no maintainer approve** (user decision 2026-10-22; [`AGENTS.md` → Merge gates](../AGENTS.md#merge-gates)). A self-PASS counts only across Bots: the account is shared, and the implementer never reviews its own pull request. The gate is the review: green `tests-ok` at the tip, a PASS at that same tip, and a test that fails when the change is reverted. Work that meets neither the defect nor the routine-need bar gets no PR at all (AGENTS.md). An issue that already has a pull request keeps it: update that branch (re-base, `update-branch`, or a commit on its head) rather than opening a second PR; a branch that cannot be pushed is the only exception, and the reason goes on the issue. 5. Budget is not set. Every cost cap will live in one config file (open PRs, attempts per PR, @@ -26,7 +26,7 @@ Decisions by the maintainer, and what follows from them: ``` issue (agent-task form) --maintainer or maintenance routine adds agent-ready--> agent (outside Actions) --branch + PR--> - Actions (GITHUB_TOKEN, no LLM): tests-ok, risk tier --T0/T1--> merge queue --T2/T3--> human approves + PASS + Actions (GITHUB_TOKEN, no LLM): tests-ok, risk tier --T0/T1--> merge queue --T2/T3--> PASS at the tip ``` | Role | Runs where | Credential | Can do | @@ -34,7 +34,7 @@ issue (agent-task form) --maintainer or maintenance routine adds agent-ready--> | Controller: tier, labels, enable auto-merge, watchdog | Actions, base-branch code, no PR code run | `GITHUB_TOKEN`, per-job least permissions | label, comment, `gh pr merge --auto --squash` | | Agents: implement, review | Grok Bot routines on the box (the owner's maintenance routine runs as `Birfy`) | the maintainer account's own `gh` login; a narrower credential remains the goal | triage and set `agent-ready`, push a branch or fork, open a PR, comment, review, enqueue a PR that may merge | | Merge | the required GitHub merge queue on `main` (ruleset `main merge queue`) | none: the queue merges when the required checks pass on its entry | squash only (set by the queue) | -| Maintainers (`Birfy`, `hsliuustc0106`) | GitHub | own accounts | approve T2/T3 (a human), set `agent-ready`, hold, pause | +| Maintainers (`Birfy`, `hsliuustc0106`) | GitHub | own accounts | `hold` (human-only), set `agent-ready`, pause; no T2/T3 approve | The owner's routines run as the maintainer account `Birfy`; that is how things work today, and it is allowed for the actions in this document. Agents must **never admin-merge, bypass a gate or branch protection, or change rulesets** @@ -48,7 +48,7 @@ with that login (a merge goes only through the merge queue). A narrower credenti | Events it causes do not start workflows (except `workflow_dispatch` and `repository_dispatch`) | a label, comment, push or PR made by an Action does not trigger other workflows; chains of "label, then workflow" do not work | do the whole decision in one job; poll on `schedule`; chain explicitly with `gh workflow run` (dispatch) | | A merge or tag made with it may not start `push` workflows (the `Test` run on `main`, `release.yml`) | documented rule, not tested here (see [Merge plumbing](#merge-plumbing-merge_group-merge-queue-auto-merge)): an auto-merge **enabled** with `GITHUB_TOKEN` is reported to merge silently | enable auto-merge with the agent's own credential, never with `GITHUB_TOKEN`; keep `release.yml` waiting for a green run on the tagged commit, and start `Test` by `workflow_dispatch` on `main` if the push run is missing | | Read-only and no secrets on `pull_request` from forks | a `pull_request` job cannot label or enable auto-merge for a fork PR | run writers from `schedule` or `pull_request_target` with **base-branch code that never checks out or runs PR code** and reads the PR through the API only | -| Cannot approve PRs (`can_approve_pull_request_reviews` is off here) | the controller can never approve T3 | intended: a human approves | +| Cannot approve PRs (`can_approve_pull_request_reviews` is off here) | the controller can never approve T3 | harmless: T2/T3 need no approve (user decision 2026-10-22) | | Cannot change `.github/workflows/**` | an Action cannot edit workflows | intended | | About 1,000 API requests per hour per repository | a chatty controller stalls | few calls per tick, filter by label | | `schedule` can lag by minutes and public repos' schedules stop after 60 days without activity | polling is not instant | acceptable; `workflow_dispatch` to run now | @@ -95,10 +95,10 @@ Generated files (`core/client.js`, `patches/**`) are not read or counted; `patch | Tier | Rule (owner decisions 2026-10-19) | Examples | | --- | --- | --- | -| T3 | a PASS at the tip (self-PASS counts) and green CI; the gating agent merges it itself through the queue (owner decision 2026-10-21) | `.github/**`, `AGENTS.md`, `package.json`, `ds-bot/**`, trust-boundary host files (`authz`, `channel`, `reach`, ...), `plugins/social/src/**`; `child_process`, `eval`, new URL hosts, token env vars, `.skip`/`.only`, fence/sanitize/display-name code; over the size cap | -| T2 | a PASS at the tip (self-PASS counts) and green CI; the gating agent merges it itself | other `core/src/host/**`, `plugins/**`, unknown paths | -| T1 | merge queue on green CI; no PASS, no approval | `core/src/client/**`, tests, generated files | -| T0 | merge queue on green CI; no PASS, no approval | `docs/**` (except policy docs), READMEs | +| T3 | a PASS at the tip (a self-PASS counts across Bots) and green CI; the gating agent merges it itself through the queue; **no maintainer approve** (user decision 2026-10-22; [`AGENTS.md` → Merge gates](../AGENTS.md#merge-gates)) | `.github/**`, `AGENTS.md`, `package.json`, `ds-bot/**`, trust-boundary host files (`authz`, `channel`, `reach`, ...), `plugins/social/src/**`; `child_process`, `eval`, new URL hosts, token env vars, `.skip`/`.only`, fence/sanitize/display-name code; over the size cap | +| T2 | a PASS at the tip (a self-PASS counts across Bots) and green CI; the gating agent merges it itself; **no maintainer approve** ([`AGENTS.md` → Merge gates](../AGENTS.md#merge-gates)) | other `core/src/host/**`, `plugins/**`, unknown paths | +| T1 | merge queue on green CI; no PASS | `core/src/client/**`, tests, generated files | +| T0 | merge queue on green CI; no PASS | `docs/**` (except policy docs), READMEs | The rules are in `.github/agent/risk-rules.json`; changing them is T3. Tests may quote URLs and fence words without a hit; `.skip`/`.only` still count in tests. @@ -112,8 +112,7 @@ node .github/scripts/risk-gate.mjs --replay 30 --repo ThinkFlowLab/FlyBot # ti **Replay when this was written** (last 30 merged PRs, #41 to #96): T0 1, T1 2, T2 2, **T3 25**. Of the 25: 14 trust-boundary paths, 7 CI or agent-rule paths, 2 content hits only, 2 size only. -So for FlyBot's current mix most PRs need the human click; the share that does not grows only if the -trust-boundary code is split out. Review the tiers by hand before enforcing anything: the four PRs +So for FlyBot's current mix most PRs are T3 and carry a PASS; no tier needs a human click (user decision 2026-10-22). Review the tiers by hand before enforcing anything: the four PRs decided by content or size alone (#60, #63, #74, #76) are the ones to argue about. ### Security of the report workflow @@ -142,8 +141,9 @@ What counts as a reviewed tip, all of it: 1. A pull request **comment or review body** by a login listed in `.github/agent/review-policy.json`. 2. Not the pull request's **own author**, **unless** that author is listed in the policy's `selfReview` (today: `Birfy`). Owner decision 2026-10-19: a PR opened by the owner's Bot/agent may be PASSed by the owner's own reviewer - Bot/agent, so no independent reviewer is required. Any other author still needs someone else. A self-PASS does not - replace green CI, and a new commit voids it. + Bot/agent, so no independent reviewer is required. That is an exception about the shared *account*, not about the + Bot: an implementer never reviews its own pull request ([`AGENTS.md` → Merge gates](../AGENTS.md#merge-gates)). Any + other author still needs someone else. A PASS does not replace green CI, and a new commit voids it. 3. The conclusion format is fixed (#637): the **first line** is `Verdict: PASS at ` (or `INC` / `REQUEST_CHANGES`), the second is the attribution (`Written-by: Reviewer`; a relay adds `Posted-by: Chief (proxy…)` on the third line, and the older prose `Written by: …` reads the same way), then the evidence — `Ran:` (or a `Ran …:` heading over a fenced block of `$ ` commands, #660–#663) with at least one command; `Not run:` naming any gap is recommended but not checked (owner decision 2026-10-22 09:57 Asia/Shanghai). A body whose first line is not the verdict is no conclusion. The comment's `author_association` must be `OWNER`, `MEMBER` or `COLLABORATOR`: a conclusion from anyone else counts for nothing, so it can neither pass a tip nor hold one (#676). 4. The sha on that first line **is the head being judged**. A later commit moves the tip and the old PASS stops counting; that report is then listed as "not counted", with the sha it named. @@ -179,7 +179,8 @@ token. It is **not** a required check and must stay out of branch protection and without a PASS still needs its own review before the gating agent merges it. **Why report-only.** The owner's reviewer Bot/agent acts as `Birfy`, which is in the policy (and in `selfReview`), so -its report can satisfy the gate, including on its own PRs. Nothing here blocks or merges: it is evidence a human or +its report can satisfy the gate on a pull request the same account opened — the review comes from a different Bot than +the implementer ([`AGENTS.md` → Merge gates](../AGENTS.md#merge-gates)). Nothing here blocks or merges: it is evidence an agent reads, and the record a later enforcement step is checked against. ## Merge plumbing: `merge_group`, merge queue, auto-merge @@ -231,7 +232,8 @@ and the `test` job never report there and the entry waits until the queue's time | Repo | public, owned by an Organization | | Merge methods | squash only; `delete_branch_on_merge: true`; `allow_auto_merge: true`; `allow_update_branch: true` | | Branch protection on `main` | required checks: the `test` job and `tests-ok`; `strict: false`; no required reviews; `enforce_admins: false`; no force pushes | -| Rulesets | `main merge queue` on `refs/heads/main`, active, no bypass actors: merge queue (squash, build and merge 1 entry at a time, 5 min wait to fill a group, 60 min check timeout, `ALLGREEN`) and required checks `ubuntu-latest · Node 24 · DSH 0.2.0-rc.2` (the `test` job) and `tests-ok`. The organization-level list needs the `admin:org` scope (not read) | +| Required check on the queue | `merge-gate` is a required check of the `main merge queue` ruleset (user decision 2026-10-22, #639), so it is the mechanical stop for anything that tries to skip the queue: `merge-gate.mjs` re-judges the pull request head on every entry | +| Rulesets | `main merge queue` on `refs/heads/main`, active, no bypass actors: merge queue (squash, build and merge 1 entry at a time, 5 min wait to fill a group, 60 min check timeout, `ALLGREEN`) and required checks `ubuntu-latest · Node 24 · DSH 0.2.0-rc.2` (the `test` job), `tests-ok` and `merge-gate` (job name, added 2026-10-22). The organization-level list needs the `admin:org` scope (not read) | | Org plan | not readable: `plan` is `null` without `admin:org` | | Workflow token | default `read`; Actions cannot approve PRs |