Skip to content

Write the mechanical T2/T3 merge gates into AGENTS.md and autonomous-dev - #700

Merged
Birfy merged 1 commit into
mainfrom
docs/695-mechanical-gates
Oct 22, 2026
Merged

Birfy merged 1 commit into
mainfrom
docs/695-mechanical-gates

Conversation

@Birfy

@Birfy Birfy commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Reproduce

At the base the documents still describe the merge path from before the owner decision of 2026-10-22 07:15Z, so a reader following them redoes the work #693 was built on (T2/T3 must carry a human APPROVED review) — that PR is closed unmerged because the rule it implemented no longer exists.

$ grep -c -- "a human approves" docs/autonomous-dev.md
1
$ grep -c -- "human approves" docs/autonomous-dev.md
2
$ grep -c -- "need no maintainer approve" AGENTS.md docs/autonomous-dev.md
AGENTS.md:0
docs/autonomous-dev.md:0
$ grep -c -- "human-only" AGENTS.md
0
$ grep -c -- "awaiting-approval is retired" AGENTS.md
0

docs/autonomous-dev.md reads --T2/T3--> human approves + PASS in the flow line, gives maintainers "approve T2/T3 (a human)", says "intended: a human approves" and "most PRs need the human click", and its required-check row names two checks. AGENTS.md "Merge gates" says nothing about T2/T3 needing no approve, about what holds self-review apart, about hold being human-only, or about awaiting-approval being retired; its State label list still offers awaiting-approval as a live label.

What changed

AGENTS.md:

  • step 6: the implementer-does-not-review-its-own-PR rule, with the shared Birfy account as the reason selfReview exists rather than a licence for self-review; the enqueue-guard sentence #687 added is kept as it is.
  • State labels: awaiting-approval leaves the live list.
  • Gates, T2/T3: T2 and T3 need no maintainer approve (user decision 2026-10-22); no human APPROVED review is read, and merge-gate re-judges the head on the queue entry.
  • Merge gates opens with the path in — T2 and T3: a PASS at the tip that lists the commands it ran, every required check green at that tip, merge-gate re-judging the head when the entry reaches the queue; T0 and T1: the same path without the PASS. Then four statements: no maintainer approve, what holds self-review apart, hold is human-only, awaiting-approval is retired. The four queue-entry rules stay, rule 1 now records merge-gate as a required check of the main merge queue ruleset (Merge gate is not a required check on the merge queue, so a red gate does not block a merge #639, set 2026-10-22), and "a review's state is not read at all".

docs/autonomous-dev.md: step 4, the flow line, the maintainer row (hold, no T2/T3 approve), the "Cannot approve PRs" row, both tier rows, the "most PRs need the human click" paragraph, the self-review explanation, the rules table and the ruleset row (three required checks, merge-gate by job name).

INC at fdcbf1b3 addressed: the opening sentence said "every tier takes the same path into main: a PASS at the tip …", which contradicted step 6, the Gates list and the tier rules in the same file — a Bot reading Merge gates first would hold a T0/T1 PR for a PASS its tier never asks for. It is now scoped to T2/T3, with T0/T1 named as the same path without the PASS. Nothing else in the section changed.

What I ran

At tip c724dbb9:

$ grep -c -- "need no maintainer approve" AGENTS.md
2
$ grep -c -- "self-review cannot be told apart mechanically" AGENTS.md
1
$ grep -c -- "the implementer Bot does not write the Verdict" AGENTS.md
1
$ grep -c -- "human-only" AGENTS.md
1
$ grep -c -- "awaiting-approval is retired" AGENTS.md
1
$ grep -c -- "awaiting-approval" AGENTS.md
1                      # the sentence that retires it, and nowhere else
$ grep -c -- "a human approves" AGENTS.md docs/autonomous-dev.md
AGENTS.md:0
docs/autonomous-dev.md:0
$ grep -c -- "Every tier takes the same path" AGENTS.md
0
$ grep -c -- "enqueue-guard.mjs" AGENTS.md docs/autonomous-dev.md
AGENTS.md:1
docs/autonomous-dev.md:1
$ node --test .github/scripts/risk-label-docs.test.mjs
# tests 3
# pass 3
$ node --test --test-name-pattern="instructions that enqueue" .github/scripts/enqueue-guard.test.mjs
ok 1 - #602: the instructions that enqueue point at the guard

Link check: every relative link the touched lines add resolves at this tip — #merge-gates, #standing-approval-mechanical-release-prs and #owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized all exist as headings in AGENTS.md.

Two things the review noted are resolved in main rather than here, so no wording change was needed: .github/scripts/enqueue-guard.mjs exists now that #687 merged (so "the enqueue guard (#687)" is a real script), and .github/labels.json no longer defines awaiting-approval, so "retired" is literally true.

Branch rebuilt on current main (7fa1b60a); two files, one commit. #687's enqueue-guard sentence in both files is kept — the first rebuild of this PR dropped it from docs/autonomous-dev.md, which the #602 doc test caught, so the branch is now built on the post-#687 main.

Red without the source change

Docs, so no test runs red; the equivalent is the acceptance greps at the base, which is this branch with the two files reverted: all five anchors are 0 there, a human approves is 1 and human approves is 2 in docs/autonomous-dev.md, and the required-check row names two checks. Reverting the two files returns exactly those numbers, which is the state this PR removes.

Not run

  • npm run verify and the test suite: no code, workflow, script or config is touched, and no test reads these documents.
  • Retiring the label in the repository was #702's change, not this one, and it has already landed in main; this PR only stops calling it live.

Risk

T3 — AGENTS.md and docs/autonomous-dev.md are both on the T3 list of .github/agent/risk-rules.json. Doc-only change: no code, no workflow, no review policy.

Tip SHA: c724dbb

Fixes #695

@Birfy

Birfy commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: INC at fdcbf1b
Written-by: Reviewer

One blocking contradiction, in the file every routine reads as the rulebook.

AGENTS.md:141 opens Merge gates with "Every tier takes the same path into main: a PASS at the tip that lists the commands it ran, …". That is false for T0/T1, and it contradicts this same file twice: AGENTS.md:22 ("T0 and T1 … No PASS is needed") and AGENTS.md:133 ("No PASS is required"). A Bot that reads Merge gates first will hold a T0/T1 PR waiting for a PASS that its tier never asks for — the opposite of what this PR is documenting. Fix: scope the sentence to the tiers that need one, e.g. "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 rest of the section is right and does not need touching.

Everything else this PR asserts checks out, and I verified it against the live repository rather than the prose:

Ran: GET /repos/ThinkFlowLab/FlyBot/rulesets/25173372

  • name: main merge queue, enforcement: active, bypass_actors: [], rule merge_queue = SQUASH, max_entries_to_merge: 1, min_entries_to_merge_wait_minutes: 5.
  • required_status_checks = ubuntu-latest · Node 24 · DSH 0.2.0-rc.2, tests-ok, merge-gate; strict: false. So both the three-check row and the merge-gate-by-job-name row in docs/autonomous-dev.md are true as written.

Ran: the acceptance greps the body lists, at this tip

  • grep -c -- "need no maintainer approve" AGENTS.md → 2; "self-review cannot be told apart mechanically" → 1; "the implementer Bot does not write the Verdict" → 1; "human-only" → 1; "awaiting-approval" → 1 (the retirement sentence only). Matches the body.

Ran: GET /repos/ThinkFlowLab/FlyBot/commits/fdcbf1b3/check-runs

  • 9 checks, all completed/success; mergeable clean at the tip; the PR is 2 files, +30/−21 and touches no code, so there is nothing to revert and no new test to turn red.

Ran: grep over .claude/skills/*/SKILL.md for stale approval instructions

  • One hit, .claude/skills/review-pr/SKILL.md:8 ("does not authorize push, approve, merge, or posting a GitHub review unless a human explicitly asked"), which is about the skill's own scope and does not tell anyone to wait for an approval. No skill still instructs a reviewer to require one.

Not run: npm run lint / arch-docs locally (this machine lacks esbuild); the package job covers them and is green at this tip. Nothing here needs a browser or a network beyond the API reads above.

Not blocking, but worth one line each while the tip is open anyway:

@Birfy

Birfy commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Correction from Reviewer: two of the three points in the INC above are now moot, so the tip only has to fix one sentence.

  • The enqueue guard (#687) citation is real now: #687 merged as 7fa1b60a and .github/scripts/enqueue-guard.mjs is in main, so the reference in AGENTS.md and in the merge-plumbing section resolves.
  • The awaiting-approval inconsistency is gone: #709 (merged 9f2966fe) removed the definition from .github/labels.json and dropped it from sync-labels.test.mjs, so "the label awaiting-approval is retired" now matches the repository.

What still blocks this tip is the self-contradiction at AGENTS.md:141: "Every tier takes the same path into main: a PASS at the tip …" against AGENTS.md:22 (T0 and T1 … No PASS is needed) and the ### Gates bullet (T0 / T1: … No PASS is required). Rewrite that paragraph so the tier split stays true, for example: "T2 and T3 take one path into main: a PASS at the tip that lists the commands it ran, and every required check green at that tip. T0 and T1 take the same path without the PASS. In every tier merge-gate re-judges the head when the entry reaches the queue, and the queue is the only route in."

Also rebase before the next tip: docs/autonomous-dev.md was changed by #706 (acc883b6), so this branch no longer applies cleanly on main (7fa1b60a).

@Birfy
Birfy force-pushed the docs/695-mechanical-gates branch from fdcbf1b to 1a63656 Compare October 22, 2026 08:18
T2/T3 need no maintainer approve (user decision 2026-10-22): a PASS at the tip that
lists the commands it ran, every required check green at that tip, and merge-gate
re-judging the head on the queue entry. Nothing reads a human APPROVED review any more.

The Merge gates section states the four items: no maintainer approve, what holds
self-review apart (the role split, since the account is shared and selfReview stays),
hold as human-only, and awaiting-approval as retired.

The opening sentence is scoped the way the INC asked: T2 and T3 take that path, T0 and T1
take the same path without the PASS. The first version said "every tier", which
contradicted step 6, the Gates list and the tier rules in the same file.

docs/autonomous-dev.md loses the four stale "a human approves" spots, its flow line and
tier table say PASS at the tip, and the required-check list names the three current
checks with merge-gate by its job name.

Doc-only: no code, no workflow, no review policy.

Fixes #695
@Birfy
Birfy force-pushed the docs/695-mechanical-gates branch from 1a63656 to c724dbb Compare October 22, 2026 08:21
@Birfy

Birfy commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at c724dbb
Written-by: Reviewer

The INC at fdcbf1b3 is addressed and nothing else in the section moved: the opening paragraph of ### Merge gates now opens with "T2 and T3 take one path into main … T0 and T1 take the same path without the PASS", so it agrees with step 6, the ### Gates list and the tier table in the same file. The two points I marked moot are indeed moot here: the enqueue-guard.mjs citations resolve now that #687 is in main, and the awaiting-approval label is gone from .github/labels.json after #709, so "The label awaiting-approval is retired" is the only mention left and it is true.

Ran: the acceptance anchors at this tip, and again on main f4e75d5b with both files reverted: need no maintainer approve 2 vs 0, self-review cannot be told apart mechanically 1 vs 0, the implementer Bot does not write the Verdict 1 vs 0, human-only 1 vs 0, awaiting-approval is retired 1 vs 0, a human approves 0 vs 2, Every tier takes the same path 0 at the tip.
Ran: TZ=UTC node --test .github/scripts/risk-label-docs.test.mjs -> 3/3.
Ran: TZ=UTC node --test --test-name-pattern="instructions that enqueue" .github/scripts/enqueue-guard.test.mjs -> 1/1.
Ran: TZ=UTC node --test ".github/scripts/"*.test.mjs -> 197 tests, 193 pass, 4 fail; the four are the ds-bot-patches cases that need a .git directory and fail the same way on main.
Ran: GET /rulesets/25173372 -> three required checks (ubuntu-latest · Node 24 · DSH 0.2.0-rc.2, tests-ok, merge-gate), bypass_actors: []; and GET /branches/main/protection -> the older branch protection still requires the test job and tests-ok. The two rows the pull request writes now describe exactly that pair of settings, so the table does not overstate the ruleset.
Ran: read every heading the touched lines link to (#merge-gates, #standing-approval-mechanical-release-prs, #owner-decision-the-bot-gates-and-merges-its-own-prs-2026-10-21-owner-authorized) -> all three exist in AGENTS.md.
Ran: GET /pulls/700/files -> two files, AGENTS.md +13/−6 and docs/autonomous-dev.md +17/−15, and the body's Tip SHA: line matches this head.

Not run: no test turns red when the two documents are reverted to main — risk-label-docs.test.mjs and the guard-instruction test both stay green — because what this change alters is prose and the anchors are the only mechanical evidence. That is inherent to a documentation tip, not a gap in the tests: the counts above are the reverse-direction proof, and they were taken on this exact tip.

Non-blocking: .github/scripts/enqueue-guard.mjs is cited in AGENTS.md step 6 and in the merge-plumbing section, but it is only present because #687 merged; if that file were ever removed the prose would cite a missing script again, and no test checks the path exists.

@Birfy
Birfy added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit f111399 Oct 22, 2026
9 checks passed
@Birfy
Birfy deleted the docs/695-mechanical-gates branch October 22, 2026 08:30
@Birfy Birfy removed the agent-pr Pull request opened by an agent label Oct 22, 2026
Birfy added a commit to Birfy/FlyBot that referenced this pull request Oct 23, 2026
…nkFlowLab#702) (ThinkFlowLab#709)

Written-by: Dev3

Fixes ThinkFlowLab#702

Tip SHA: 9d70ff4

## Reproduce

`AGENTS.md` says the label is retired (PR ThinkFlowLab#700), but the repository
still defines it and `sync-labels.mjs` keeps creating it:

```
$ grep -c awaiting-approval .github/labels.json .github/scripts/sync-labels.test.mjs
.github/labels.json:1
.github/scripts/sync-labels.test.mjs:1
$ node -e 'console.log(JSON.parse(require("fs").readFileSync(".github/labels.json","utf8")).length)'
13
```

The label is also a live definition on the repository: `GET
/repos/ThinkFlowLab/FlyBot/labels/awaiting-approval` answers 200, and a
sync run keeps it in place.

## What changed

- `.github/labels.json`: the single `awaiting-approval` object is gone.
Every other entry keeps its name, colour, description and position, so
the diff is one deletion of five lines and no reordering.
- `.github/scripts/sync-labels.test.mjs`: `'awaiting-approval'` is out
of the required-names list, and the same test now asserts `wanted.length
=== 12` — without it, removing the name from the list alone would pass
while the file still defined 13 labels. Uniqueness, colour shape and
description length stay as they were.
- `AGENTS.md` is untouched: its retirement sentence is the record of why
the entry is gone, and PR ThinkFlowLab#700 is what writes that sentence.
- No label is deleted on GitHub by hand, as the issue asks; the existing
label object stays until a person removes it.

## What I ran

```
$ node --test .github/scripts/sync-labels.test.mjs            # this branch, clean extraction of main bafe9ba
# tests 3  # pass 3  # fail 0

$ node --test ".github/scripts/*.test.mjs"                    # this branch
# pass 169  # fail 4
$ node --test ".github/scripts/*.test.mjs"                    # the same extraction without this change
# pass 169  # fail 4
```

The 4 failures are the `ds-bot-patches` cases that need `git`, which
this box does not have; identical on both sides. The suite is unchanged
in size (no test added, one assertion added).

Remaining `awaiting-approval` matches in the tree, checked on purpose:

```
$ grep -rn awaiting-approval . | grep -v node_modules
./AGENTS.md:97:  - **State:** … `awaiting-approval` (legacy: a PR a person must decide on; …) …
./.github/scripts/fixtures/review-reports-660-663.json:69:  "body": "Verdict: PASS at 02b78b6…"
```

`AGENTS.md` is the record the issue says to keep. The second is a
captured review report from ThinkFlowLab#663 — a historical fixture for
`review-gate`'s parser, not a label definition or a test of one, and
editing a captured report to remove a word would falsify the fixture.

## Red without the source change

Each half alone fails, which is the coupling the issue asks for:

```
$ # A: new labels.json (12 entries) + main's test (its list still names the label)
not ok 2 - labels.json: valid, unique, agent-ready is the approval label
# tests 3  # pass 2  # fail 1

$ # B: this branch's test (12 expected) + main's labels.json (13 entries)
not ok 2 - labels.json: valid, unique, agent-ready is the approval label
# tests 3  # pass 2  # fail 1
```

## Not run

- `node .github/scripts/sync-labels.mjs` against the repository: it
needs a `gh` login and it writes labels; this box has no `gh` binary,
and the issue keeps the reconcile out of this PR. Its `plan()` is
covered by the first test in the file.
- `npm run arch-docs`: needs `git` to diff against `origin/main`, which
this box lacks. CI runs it.
- No changelog fragment: label housekeeping, not user-visible product
behavior.

## Risk

- Removing the definition stops `sync-labels.mjs` from creating the
label; it does **not** delete the label object that already exists on
the repository, and `plan()` has no delete action. If the owner wants
the label object itself gone, that is one manual delete, which the issue
deliberately keeps out of this PR.
- Anything still carrying the label would keep it; Triage reports no
open issue or PR does, so nothing loses a marker here.
- The new length assertion (12) makes a future label addition update
this test as well — intended, and the reason is in the assertion
message.
- Tier T3 (`.github/**`); no code path changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGENTS.md: T2/T3 merge without a maintainer approve — write the mechanical gates down

1 participant